From efe3efa7c95c24fb5e5b353a240954aa0f41c0e2 Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 19 Jan 2018 19:16:24 -0800 Subject: [PATCH] avoid dynamic connections to store --- modules/ui/components/NeverUpdate.jsx | 13 +++++ modules/ui/connect.jsx | 52 ++++++++++--------- web/app/cad/actions/actionButtonBehavior.js | 18 +++---- .../cad/dom/components/PlugableControlBar.jsx | 21 ++++---- web/app/cad/dom/menu/MenuHolder.jsx | 43 ++++++++------- 5 files changed, 84 insertions(+), 63 deletions(-) create mode 100644 modules/ui/components/NeverUpdate.jsx diff --git a/modules/ui/components/NeverUpdate.jsx b/modules/ui/components/NeverUpdate.jsx new file mode 100644 index 00000000..780e7b1f --- /dev/null +++ b/modules/ui/components/NeverUpdate.jsx @@ -0,0 +1,13 @@ +import React, {Fragment} from 'react'; + +export default class NeverUpdate extends React.Component { + + shouldComponentUpdate() { + return false; + } + + render() { + return {this.props.children}; + } + +} \ No newline at end of file diff --git a/modules/ui/connect.jsx b/modules/ui/connect.jsx index 712fd8c4..9f1f6dd2 100644 --- a/modules/ui/connect.jsx +++ b/modules/ui/connect.jsx @@ -1,12 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import shallowEqual from "../gems/shallowEqual"; -export default function connect(WrappedComponent, tokens, {staticProps, mapProps, mapActions}) { - - if (!Array.isArray(tokens)) { - tokens = [tokens]; - } +export default function connect(WrappedComponent, tokens, {staticProps, mapProps, mapActions, mapSelfProps}) { mapProps = createMapper(mapProps); @@ -14,20 +9,30 @@ export default function connect(WrappedComponent, tokens, {staticProps, mapProps return dispatch; }; - return class StateConnector extends React.Component { + mapSelfProps = mapSelfProps || (() => undefined); + + return class StateConnector extends React.PureComponent { - constructor(context) { + constructor(props) { super(); this.mounted = false; this.stateProps = {}; - this.dispatchProps = mapActions(this.dispatch); + this.dispatchProps = mapActions(this.dispatch, props); } componentWillMount() { - this.externalStateConnection = this.context.bus.connectToState(tokens, this.setExternalState); + this.externalStateConnection = this.context.bus.connectToState(this.getTokens(), this.setExternalState); this.externalStateConnection(); } + getTokens() { + let tokensArr = tokens instanceof Function ? tokens(this.props) : tokens; + if (!Array.isArray(tokensArr)) { + tokensArr = [tokensArr]; + } + return tokensArr; + } + componentDidMount() { this.mounted = true; } @@ -38,23 +43,21 @@ export default function connect(WrappedComponent, tokens, {staticProps, mapProps } setExternalState = (state) => { - this.stateProps = mapProps(state); + this.stateProps = mapProps(state, this.props); if (this.mounted) { this.forceUpdate(); } }; - shouldComponentUpdate(nextProps, nextState) { - return !shallowEqual(this.props, nextProps); - - } - dispatch = (event, data) => { this.context.bus.dispatch(event, data); }; render() { - return + return } componentDidCatch() { @@ -66,13 +69,9 @@ export default function connect(WrappedComponent, tokens, {staticProps, mapProps } } -function createMapper(mapper) { +function createMapper(mapper, comp) { if (!mapper) { - return function (state) { - let props = {}; - state.forEach(stateItem => Object.assign(props, stateItem)); - return props; - }; + return DEFAULT_MAPPER; } else if (Array.isArray(mapper)) { return function (state) { let props = {}; @@ -87,5 +86,8 @@ function createMapper(mapper) { return mapper; } - - +export function DEFAULT_MAPPER(state) { + let props = {}; + state.forEach(stateItem => Object.assign(props, stateItem)); + return props; +} diff --git a/web/app/cad/actions/actionButtonBehavior.js b/web/app/cad/actions/actionButtonBehavior.js index 9b7d3e29..5eed39cc 100644 --- a/web/app/cad/actions/actionButtonBehavior.js +++ b/web/app/cad/actions/actionButtonBehavior.js @@ -1,12 +1,12 @@ - import {TOKENS as ACTION_TOKENS} from "./actionSystemPlugin"; -export function mapActionBehavior(actionId) { - let actionRunToken = ACTION_TOKENS.actionRun(actionId); - - return dispatch => ({ - onClick: data => dispatch(actionRunToken, data), - onMouseEnter: ({pageX, pageY}) => dispatch(ACTION_TOKENS.SHOW_HINT_FOR, [actionId, pageX, pageY]), - onMouseLeave: () => dispatch(ACTION_TOKENS.SHOW_HINT_FOR, null) - }); +export function mapActionBehavior(actionIdProp) { + return (dispatch, props) => { + const actionId = props[actionIdProp]; + const actionRunToken = ACTION_TOKENS.actionRun(actionId); + return { + onClick: data => dispatch(actionRunToken, data), + onMouseEnter: ({pageX, pageY}) => dispatch(ACTION_TOKENS.SHOW_HINT_FOR, [actionId, pageX, pageY]), + onMouseLeave: () => dispatch(ACTION_TOKENS.SHOW_HINT_FOR, null) + }}; } \ No newline at end of file diff --git a/web/app/cad/dom/components/PlugableControlBar.jsx b/web/app/cad/dom/components/PlugableControlBar.jsx index 8839beb5..4ac6fff7 100644 --- a/web/app/cad/dom/components/PlugableControlBar.jsx +++ b/web/app/cad/dom/components/PlugableControlBar.jsx @@ -6,6 +6,7 @@ import {TOKENS as UI_TOKENS} from '../uiEntryPointsPlugin'; import {TOKENS as ACTION_TOKENS} from '../../actions/actionSystemPlugin'; import {toIdAndOverrides} from "../../actions/actionRef"; import {mapActionBehavior} from "../../actions/actionButtonBehavior"; +import {DEFAULT_MAPPER} from "../../../../../modules/ui/connect"; export default function PlugableControlBar() { @@ -15,15 +16,7 @@ export default function PlugableControlBar() { function ButtonGroup({actions}) { return actions.map(actionRef => { let [id, overrides] = toIdAndOverrides(actionRef); - let Comp = connect(ActionButton, - [ACTION_TOKENS.actionAppearance(id), ACTION_TOKENS.actionState(id)], - { - staticProps: {actionId: id}, - mapProps: ([appearance, state]) => Object.assign({}, appearance, state, overrides), - mapActions: mapActionBehavior(id) - } - ); - return ; + return ; }); } @@ -56,6 +49,16 @@ const BUTTON_CONNECTOR = { const LeftGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_LEFT, BUTTON_CONNECTOR); const RightGroup = connect(ButtonGroup, UI_TOKENS.CONTROL_BAR_RIGHT, BUTTON_CONNECTOR); + +const ConnectedActionButton = connect(ActionButton, + props => [ACTION_TOKENS.actionAppearance(props.actionId), + ACTION_TOKENS.actionState(props.actionId)], + { + mapProps: (state, props) => Object.assign(DEFAULT_MAPPER(state), props), + mapActions: mapActionBehavior('actionId'), + } +); + function getMenuData(el) { //TODO: make more generic return { diff --git a/web/app/cad/dom/menu/MenuHolder.jsx b/web/app/cad/dom/menu/MenuHolder.jsx index f1e8eaa8..7c6d9824 100644 --- a/web/app/cad/dom/menu/MenuHolder.jsx +++ b/web/app/cad/dom/menu/MenuHolder.jsx @@ -6,33 +6,20 @@ import Menu, {MenuItem, MenuSeparator} from "../../../../../modules/ui/component import Fa from "../../../../../modules/ui/components/Fa"; import Filler from "../../../../../modules/ui/components/Filler"; import {TOKENS as KeyboardTokens} from "../../keyboard/keyboardPlugin"; +import {DEFAULT_MAPPER} from "../../../../../modules/ui/connect"; function MenuHolder({menus}) { - return menus.map(({id, actions}) => { - let menuToken = MENU_TOKENS.menuState(id); - let connectedMenu = connect(ActionMenu, [menuToken, KeyboardTokens.KEYMAP], { - staticProps: {actions}, - mapProps: [,keymap => ({keymap})] - }); - return React.createElement(connectedMenu, {key: id}); - }); + return menus.map(({id, actions}) => ); } -function ActionMenu({actions, keymap, ...props}) { - return - {actions.map((action, index)=> { +function ActionMenu({actions, keymap, ...menuState}) { + return + {actions.map((action, index) => { if (action === '-') { return } - const runToken = ACTION_TOKENS.actionRun(action); - return React.createElement( - connect(ActionMenuItem, [ACTION_TOKENS.actionState(action), ACTION_TOKENS.actionAppearance(action)], { - staticProps: {hotKey: keymap[action]}, - mapActions: dispatch => ({ - onClick: () => dispatch(runToken) - }) - }), {key: action}); - })} + return ; + })} ; } @@ -61,6 +48,22 @@ function ActionMenuItem({label, cssIcons, icon32, icon96, onClick, enabled, hotK return ; } +const ConnectedActionMenu = connect(ActionMenu, + ({menuId}) => [MENU_TOKENS.menuState(menuId), KeyboardTokens.KEYMAP], + { + mapProps: ([menuState, keymap], {actions}) => Object.assign({keymap, actions}, menuState) + }); + + +let ConnectedMenuItem = connect(ActionMenuItem, + ({actionId}) => [ACTION_TOKENS.actionState(actionId), ACTION_TOKENS.actionAppearance(actionId)], + { + mapActions: (dispatch, {actionId}) => ({ + onClick: () => dispatch(ACTION_TOKENS.actionRun(actionId)) + }), + mapSelfProps: ({hotKey}) => ({hotKey}) + } +); export default connect(MenuHolder, MENU_TOKENS.MENUS, { mapProps: ([menus]) => ({menus})