Search code examples
javascriptreduxreact-reduxreact-redux-form

How to avoid useless mapStateToProps()?


Assume a React + Redux application with a page that contains ~100 MUI checkboxes.

All of these are controlled using Redux state, like so:

let CheckboxComponent = React.createClass({

  shouldComponentUpdate(nextProps) {
    return nextProps.activeList !== this.props.activeList;
  }

  render() {

    let isChecked = this.props.activeList.indexOf(this.props.myId) >= 0;

    return <Checkbox
      checked={isChecked}
    />;
  }

});

function mapStateToProps(state) {
  return {
    activeList: state.form.myForm.values.activeList;
  };
}

CheckboxComponent = connect(mapStateToProps)(CheckboxComponent);

Now, let's say that I also have a controlled <input> on the same page, using the same Redux store. Each keypress in the input will cause a Redux state change. So, while typing this will cause many changes, but not for activeList.

My concern is that - if I'm not wrong - mapStateToProps and in consequence shouldComponentUpdate of each <CheckboxComponent> gets called on each keystroke - that's a few hundred useless function calls.

Fortunately, shouldComponentUpdate will avoid a useless re-render, but in practice my shouldComponentUpdate is more complex and thus slightly expensive (even if less expensive than a re-render).

All that could be solved if mapStateToProps() would not be called for Redux state changes that don't involve activeList.

Is such an optimization possible in some way?


Solution

  • There's a few different answers to this question.

    First: no, you can't avoid mapState calls for connected components that "aren't affected". By definition, Redux only has a single event emitter, and does not track what parts of the state might have changed during a dispatch. It's up to each individual subscriber to check the new state and see if there's a relevant change.

    Second: yes, dispatching a separate action for every individual keystroke isn't particularly efficient. I've handled that in my own app by writing a higher-order component that uses its state to buffer changes for faster re-rendering of inputs, while debouncing action dispatching. I have that component available in a gist - made a couple updates since then, but that's the general idea. You may also want to look at libraries such as React-Reformed, React-Form, or React-Redux-Form, which provide some similar pieces that probably have had more work put into them then my bit of code.

    Third: it looks like you're passing around an array of "active" items to each individual checkbox component, and comparing indices inside that component. That's not a very performant pattern. A better approach would probably be for the parent component to have a list of IDs, pass an ID to each connected child, and have each child look up its own data by ID in its mapState. See these slides on High-Performance Redux for more information. I also have other info on Redux Performance as part of my React/Redux links list.