diff --git a/platform/core/package.json b/platform/core/package.json index 93a9bea38..33d6f60ea 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -45,6 +45,7 @@ "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.1", "mousetrap": "^1.6.3", - "validate.js": "^0.12.0" + "validate.js": "^0.12.0", + "immer": "6.0.2" } } diff --git a/platform/core/src/redux/reducers/viewports.js b/platform/core/src/redux/reducers/viewports.js index 5396e226c..88c4fc7bd 100644 --- a/platform/core/src/redux/reducers/viewports.js +++ b/platform/core/src/redux/reducers/viewports.js @@ -1,5 +1,5 @@ import cloneDeep from 'lodash.clonedeep'; -import merge from 'lodash.merge'; +import produce from 'immer'; import { CLEAR_VIEWPORT, @@ -93,12 +93,13 @@ const viewports = (state = DEFAULT_STATE, action) => { * @return {Object} New state. */ case SET_VIEWPORT_ACTIVE: { - const activeViewportIndex = getActiveViewportIndex( - state.numRows, - state.numColumns, - action.viewportIndex - ); - return { ...state, activeViewportIndex }; + return produce(state, draftState => { + draftState.activeViewportIndex = getActiveViewportIndex( + draftState.numRows, + draftState.numColumns, + action.viewportIndex + ); + }); } /** @@ -163,21 +164,20 @@ const viewports = (state = DEFAULT_STATE, action) => { * @return {Object} New state. */ case SET_VIEWPORT: { - const layout = cloneDeep(state.layout); + return produce(state, draftState => { + draftState.viewportSpecificData[action.viewportIndex] = + draftState.viewportSpecificData[action.viewportIndex] || {}; - let viewportSpecificData = cloneDeep(state.viewportSpecificData); - viewportSpecificData[action.viewportIndex] = merge( - {}, - viewportSpecificData[action.viewportIndex], - action.viewportSpecificData - ); + Object.keys(action.viewportSpecificData).forEach(key => { + draftState.viewportSpecificData[action.viewportIndex][key] = + action.viewportSpecificData[key]; + }); - if (action.viewportSpecificData && action.viewportSpecificData.plugin) { - layout.viewports[action.viewportIndex].plugin = - action.viewportSpecificData.plugin; - } - - return { ...state, layout, viewportSpecificData }; + if (action.viewportSpecificData && action.viewportSpecificData.plugin) { + draftState.layout.viewports[action.viewportIndex].plugin = + action.viewportSpecificData.plugin; + } + }); } /** diff --git a/platform/viewer/src/components/ViewportGrid/ConnectedViewportGrid.js b/platform/viewer/src/components/ViewportGrid/ConnectedViewportGrid.js index 774b10401..be4b693c0 100644 --- a/platform/viewer/src/components/ViewportGrid/ConnectedViewportGrid.js +++ b/platform/viewer/src/components/ViewportGrid/ConnectedViewportGrid.js @@ -2,15 +2,20 @@ import ViewportGrid from './ViewportGrid.js'; import { MODULE_TYPES } from '@ohif/core'; import { connect } from 'react-redux'; import { extensionManager } from './../../App.js'; +import memoize from 'lodash/memoize'; -const mapStateToProps = state => { +const getAvailableViewportModules = memoize(viewportModules => { const availableViewportModules = {}; - const viewportModules = extensionManager.modules[MODULE_TYPES.VIEWPORT]; - viewportModules.forEach(moduleDefinition => { availableViewportModules[moduleDefinition.extensionId] = moduleDefinition.module; }); + return availableViewportModules; +}); + +const mapStateToProps = state => { + const viewportModules = extensionManager.modules[MODULE_TYPES.VIEWPORT]; + const availableViewportModules = getAvailableViewportModules(viewportModules); // TODO: Use something like state.plugins.defaultPlugin[MODULE_TYPES.VIEWPORT] let defaultPlugin; diff --git a/platform/viewer/src/components/ViewportGrid/ViewportGrid.js b/platform/viewer/src/components/ViewportGrid/ViewportGrid.js index ac17cd8c8..7aa497568 100644 --- a/platform/viewer/src/components/ViewportGrid/ViewportGrid.js +++ b/platform/viewer/src/components/ViewportGrid/ViewportGrid.js @@ -39,66 +39,68 @@ const ViewportGrid = function(props) { }); }, [viewportData]); - const ViewportPanes = layout.viewports.map((layout, viewportIndex) => { - const displaySet = viewportData[viewportIndex]; + const getViewportPanes = () => + layout.viewports.map((layout, viewportIndex) => { + const displaySet = viewportData[viewportIndex]; - if (!displaySet) { - return null; - } + if (!displaySet) { + return null; + } - const data = { - displaySet, - studies, - }; + const data = { + displaySet, + studies, + }; - // JAMES TODO: + // JAMES TODO: - // Use whichever plugin is currently in use in the panel - // unless nothing is specified. If nothing is specified - // and the display set has a plugin specified, use that. - // - // TODO: Change this logic to: - // - Plugins define how capable they are of displaying a SopClass - // - When updating a panel, ensure that the currently enabled plugin - // in the viewport is capable of rendering this display set. If not - // then use the most capable available plugin - const pluginName = - !layout.plugin && displaySet && displaySet.plugin - ? displaySet.plugin - : layout.plugin; + // Use whichever plugin is currently in use in the panel + // unless nothing is specified. If nothing is specified + // and the display set has a plugin specified, use that. + // + // TODO: Change this logic to: + // - Plugins define how capable they are of displaying a SopClass + // - When updating a panel, ensure that the currently enabled plugin + // in the viewport is capable of rendering this display set. If not + // then use the most capable available plugin + const pluginName = + !layout.plugin && displaySet && displaySet.plugin + ? displaySet.plugin + : layout.plugin; - const ViewportComponent = _getViewportComponent( - data, // Why do we pass this as `ViewportData`, when that's not really what it is? - viewportIndex, - children, - availablePlugins, - pluginName, - defaultPluginName - ); + const ViewportComponent = _getViewportComponent( + data, // Why do we pass this as `ViewportData`, when that's not really what it is? + viewportIndex, + children, + availablePlugins, + pluginName, + defaultPluginName + ); - return ( - { - setViewportData({ - viewportIndex, - StudyInstanceUID, - displaySetInstanceUID, - }); - }} - viewportIndex={viewportIndex} // Needed by `setViewportData` - className={classNames('viewport-container', { - active: activeViewportIndex === viewportIndex, - })} - key={viewportIndex} - > - {ViewportComponent} - - ); - }); + return ( + + {ViewportComponent} + + ); + }); + + const ViewportPanes = React.useMemo(getViewportPanes, [ + layout, + viewportData, + studies, + children, + availablePlugins, + defaultPluginName, + setViewportData, + activeViewportIndex, + ]); return (
diff --git a/yarn.lock b/yarn.lock index cb381a7db..10e7b245b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9922,6 +9922,11 @@ immer@1.10.0: resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d" integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg== +immer@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/immer/-/immer-6.0.2.tgz#5bc08dc4930c756d0749533a2afbd88c8de0cd19" + integrity sha512-56CMvUMZl4kkWJFFUe1TjBgGbyb9ibzpLyHD+RSKSVdytuDXgT/HXO1S+GJVywMVl5neGTdAogoR15eRVEd10Q== + immutable@>=3.6.0: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"