From bd648e3bd580a1113b0c6e5329d477f40cd04ba7 Mon Sep 17 00:00:00 2001 From: Ray Schamp <ray@scratch.mit.edu> Date: Fri, 3 Feb 2017 17:19:31 -0500 Subject: [PATCH] Implement shouldComponentUpdate on TargetPane Improves performance by comparing sprite info values (rather than object identities), and by excluding irrelevant data from this comparison. --- package.json | 3 + src/components/target-pane/target-pane.jsx | 159 +++++++++++---------- src/containers/target-pane.jsx | 10 +- 3 files changed, 96 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index 2df763fd2..a94e01058 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,9 @@ "husky": "0.13.1", "lodash.bindall": "4.4.0", "lodash.defaultsdeep": "4.6.0", + "lodash.isequal": "4.5.0", + "lodash.omit": "4.5.0", + "lodash.pick": "4.4.0", "minilog": "3.1.0", "opt-cli": "1.5.1", "postcss-loader": "1.2.2", diff --git a/src/components/target-pane/target-pane.jsx b/src/components/target-pane/target-pane.jsx index 2133444e2..7c11d7a02 100644 --- a/src/components/target-pane/target-pane.jsx +++ b/src/components/target-pane/target-pane.jsx @@ -1,3 +1,5 @@ +const isEqual = require('lodash.isequal'); +const omit = require('lodash.omit'); const React = require('react'); const VM = require('scratch-vm'); @@ -15,87 +17,98 @@ const StageSelector = require('../../containers/stage-selector.jsx'); * @param {object} props Props for the component * @returns {React.Component} rendered component */ -const TargetPane = function (props) { - const { - editingTarget, - backdropLibraryVisible, - costumeLibraryVisible, - spriteLibraryVisible, - onNewSpriteClick, - onNewCostumeClick, - onNewBackdropClick, - onRequestCloseBackdropLibrary, - onRequestCloseCostumeLibrary, - onRequestCloseSpriteLibrary, - onSelectSprite, - stage, - sprites, - vm, - ...componentProps - } = props; - return ( - <Box {...componentProps}> - <Box - alignContent="flex-start" - alignItems="flex-start" - grow={1} - style={{overflowY: 'auto'}} - > - <SpriteSelectorComponent - grow={1} - selectedId={editingTarget} - shrink={0} - sprites={sprites} - width="100%" - onSelectSprite={onSelectSprite} - /> - </Box> - <Box - direction="column" - shrink={0} - width={72} - > - {stage.id && <StageSelector - backdropCount={stage.costumeCount} - id={stage.id} - selected={stage.id === editingTarget} - shrink={0} - url={stage.costume.skin} - onSelect={onSelectSprite} - />} +class TargetPane extends React.Component { + shouldComponentUpdate (nextProps) { + return ( + // Do a normal shallow compare on all props except sprites + Object.keys(omit(nextProps, ['sprites'])) + .reduce((all, k) => all || nextProps[k] !== this.props[k], false) || + // Deep compare on sprites object + !isEqual(this.props.sprites, nextProps.sprites) + ); + } + render () { + const { + editingTarget, + backdropLibraryVisible, + costumeLibraryVisible, + spriteLibraryVisible, + onNewSpriteClick, + onNewCostumeClick, + onNewBackdropClick, + onRequestCloseBackdropLibrary, + onRequestCloseCostumeLibrary, + onRequestCloseSpriteLibrary, + onSelectSprite, + stage, + sprites, + vm, + ...componentProps + } = this.props; + return ( + <Box {...componentProps}> <Box alignContent="flex-start" alignItems="flex-start" - direction="column" grow={1} - shrink={0} + style={{overflowY: 'auto'}} > - <button onClick={onNewSpriteClick}>New Sprite</button> - {editingTarget === stage.id ? ( - <button onClick={onNewBackdropClick}>New Backdrop</button> - ) : ( - <button onClick={onNewCostumeClick}>New Costume</button> - )} - <SpriteLibrary - visible={spriteLibraryVisible} - vm={vm} - onRequestClose={onRequestCloseSpriteLibrary} - /> - <CostumeLibrary - visible={costumeLibraryVisible} - vm={vm} - onRequestClose={onRequestCloseCostumeLibrary} - /> - <BackdropLibrary - visible={backdropLibraryVisible} - vm={vm} - onRequestClose={onRequestCloseBackdropLibrary} + <SpriteSelectorComponent + grow={1} + selectedId={editingTarget} + shrink={0} + sprites={sprites} + width="100%" + onSelectSprite={onSelectSprite} /> </Box> + <Box + direction="column" + shrink={0} + width={72} + > + {stage.id && <StageSelector + backdropCount={stage.costumeCount} + id={stage.id} + selected={stage.id === editingTarget} + shrink={0} + url={stage.costume.skin} + onSelect={onSelectSprite} + />} + <Box + alignContent="flex-start" + alignItems="flex-start" + direction="column" + grow={1} + shrink={0} + > + <button onClick={onNewSpriteClick}>New Sprite</button> + {editingTarget === stage.id ? ( + <button onClick={onNewBackdropClick}>New Backdrop</button> + ) : ( + <button onClick={onNewCostumeClick}>New Costume</button> + )} + <SpriteLibrary + visible={spriteLibraryVisible} + vm={vm} + onRequestClose={onRequestCloseSpriteLibrary} + /> + <CostumeLibrary + visible={costumeLibraryVisible} + vm={vm} + onRequestClose={onRequestCloseCostumeLibrary} + /> + <BackdropLibrary + visible={backdropLibraryVisible} + vm={vm} + onRequestClose={onRequestCloseBackdropLibrary} + /> + </Box> + </Box> </Box> - </Box> - ); -}; + ); + } +} const spriteShape = React.PropTypes.shape({ costume: React.PropTypes.shape({ skin: React.PropTypes.string, diff --git a/src/containers/target-pane.jsx b/src/containers/target-pane.jsx index 119628930..c8d2aa74a 100644 --- a/src/containers/target-pane.jsx +++ b/src/containers/target-pane.jsx @@ -1,4 +1,5 @@ const bindAll = require('lodash.bindall'); +const pick = require('lodash.pick'); const React = require('react'); const {connect} = require('react-redux'); @@ -36,16 +37,19 @@ class TargetPane extends React.Component { const { onSelectSprite, // eslint-disable-line no-unused-vars - ...targetSelectorProps + ...targetPaneProps } = TargetPaneComponent.propTypes; TargetPane.propTypes = { - ...targetSelectorProps + ...targetPaneProps }; const mapStateToProps = state => ({ editingTarget: state.targets.editingTarget, - sprites: state.targets.sprites, + sprites: Object.keys(state.targets.sprites).reduce((sprites, k) => { + sprites[k] = pick(state.targets.sprites[k], ['costume', 'name', 'order']); + return sprites; + }, {}), stage: state.targets.stage, spriteLibraryVisible: state.modals.spriteLibrary, costumeLibraryVisible: state.modals.costumeLibrary, -- GitLab