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?
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.