Example code: https://github.com/d6u/example-redux-update-nested-props/blob/master/one-connect/index.js

View live demo: http://d6u.github.io/example-redux-update-nested-props/one-connect.html

How to optimize small updates to props of nested component?

I have above components, Repo and RepoList. I want to update the tag of the first repo (Line 14). So I dispatched an UPDATE_TAG action. Before I implemented shouldComponentUpdate , the dispatch takes about 200ms, which is expected since we are wasting lots of time diffing <Repo/> s that haven't changed.

After added shouldComponentUpdate , dispatch takes about 30ms. After production build React.js, the updates only cost at about 17ms. This is much better, but timeline view in Chrome dev console still indicate jank frame (longer than than 16.6ms).

Imagine if we have many updates like this, or <Repo/> is more complicated than current one, we won't be able to maintain 60fps.

My question is, for such small updates to a nested component's props, is there a more efficient and canonical way to update the content? Can I still use Redux?

I got a solution by replacing every tags with an observable inside reducer. Something like

// inside reducer when handling UPDATE_TAG action // repos[0].tags of state is already replaced with a Rx.BehaviorSubject get('repos[0].tags', state).onNext([{ id: 213, text: 'Node.js' }]);

Then I subscribe to their values inside Repo component using https://github.com/jayphelps/react-observable-subscribe. This worked great. Every dispatch only costs 5ms even with development build of React.js. But I feel like this is an anti-pattern in Redux.

Update 1

I followed the recommendation in Dan Abramov's answer and normalized my state and updated connect components

The new state shape is:

{ repoIds: ['1', '2', '3', ...], reposById: { '1': {...}, '2': {...} } }

I added console.time around ReactDOM.render to time the initial rendering.

However, the performance is worse than before (both initial rendering and updating). (Source: https://github.com/d6u/example-redux-update-nested-props/blob/master/repo-connect/index.js, Live demo: http://d6u.github.io/example-redux-update-nested-props/repo-connect.html)

// With dev build INITIAL: 520.208ms DISPATCH: 40.782ms // With prod build INITIAL: 138.872ms DISPATCH: 23.054ms

I think connect on every <Repo/> has lots of overhead.

Update 2

Based on Dan's updated answer, we have to return connect 's mapStateToProps arguments return an function instead. You can check out Dan's answer. I also updated the demos.

Below, the performance is much better on my computer. And just for fun, I also added the side effect in reducer approach I talked (source, demo) (seriously don't use it, it's for experiment only).

// in prod build (not average, very small sample) // one connect at root INITIAL: 83.789ms DISPATCH: 17.332ms // connect at every <Repo/> INITIAL: 126.557ms DISPATCH: 22.573ms // connect at every <Repo/> with memorization INITIAL: 125.115ms DISPATCH: 9.784ms // observables + side effect in reducers (don't use!) INITIAL: 163.923ms DISPATCH: 4.383ms

Update 3

Just added react-virtualized example based on "connect at every with memorization"