Redux: Redux Performance with Large Store and frequent updates

Created on 28 Jan 2016  ·  18Comments  ·  Source: reduxjs/redux

This is a question / ask for advice

Let's say you have a fairly large SPA with lots of state since there are many pages, panels, sub panels and lots of stuff.

Now imagine you design your App using reducer composition (at various levels) and have the Store pretty well organised so that it makes your life easier.

Now imagine that in one of the panels you get live events. For example, let's say you have a list of 100 items consisting of stock exchange values which are updated each one independently 1 time per second. (Please disregard the problem of the network and assume the values are randomly generated).

I believe React is the way to go since it will be smart and won't render when it is not necessary.

But I'm not sure how to make it work with Redux.

It's nice that I can use one Store and fetch all the stock values and store them individually triggering actions an so on and having a pretty nice Store with all the info very well organized.

But since I am getting very frequent updates, then all my reducers (which will be a lot) will have to be called. I know that the call will be very quick since most of the time I will only copy object pointers.

Anyway since my app is using composition so heavily then there will be many function calls. This can be bad specially on old mobile devices.

I'm designing one such app and I'm not sure how to make Redux fit in there (if possible). Perhaps you could give some valuable advice at this point (I just started working on it). Is it possible to use (somehow) different Stores each one connected only to different actions so that I minimize calls to reducers?

Any ideas?
Thanks

discussion

Most helpful comment

React Redux does not use forceUpdate() at all. It’s much more careful :-)
Yep, it won't be causing unnecessary updates.

All 18 comments

This comment and the whole discussion may help: https://github.com/rackt/redux/issues/1287#issuecomment-176330134

Thanks @sompylasar. My question is a bit different.

I'm sure that the updated values belong to the Store because they are a fundamental part of the App.

Say we have a store such as this one:

store: {
  subStore1: {
    subSubstore1: {}
    ...
    subSubstore10: {}
  },
subStore2: {
    subSubstore1: {}
    ...
    subSubstore10: {}
  }
...
subStore10: {
    subSubstore1: {}
    ...
    subSubstore10: {}
  }
}

(I hope you get the idea). In fact this could go even further in depth.

All substores correspond to very different concerns of the App and the subSubstores are handled by their own reducers.

Then I dispatch an action which I know will only update substore2:{subSubstore6}.

Why not simply copying pointers substore1, substore3, ...substore9 and now calling the reducers for the other subSubstores?

Would it be possible to to tell somehow combineReducers which possible actions will trigger new states in their child reducers?

Or perhaps I should consider using different Stores?

Any advice?

Thanks!

You might want to look at https://github.com/omnidan/redux-ignore.

I didn't know about redux-ignore and it definitely looks very promising!

Perhaps this is a possible way to go when developing very large apps (SPA) that deal with orthogonal concerns.

Thanks for the advice!

But since I am getting very frequent updates, then all my reducers (which will be a lot) will have to be called.

Let me make a important correction: There is only one reducer function on a Redux store. You can break up that function however you'd like, making a tradeoff for convenience, speed, or other factors depending on your needs. combineReducers is a common way to handle this, but it's not required.

One potentially performant idea I have is to map type to an object of handlers, keyed by type. Here's a naive way of doing it:

function buildReducer(handlers) {
  return function reducer(state = initialState, action) {
    return handlers[action.type](state, action);
  }
}

Then you would only be running the one reducer per type. There isn't a 1:1 mapping of actions to reducers, so you might need to reduce your reducers for each type, but you'll still be lowering your amount of overhead.

Of course, DOM operations and React rendering is probably going to be slower than some plain JS in your production apps. Plus most JS engines will treat your reducers as hot code and JIT compile them to native code, so I wouldn't expect it to be that slow in practice.

You can also debounce renders using redux-batched-subscribe, so in case you have multiple actions in a very short duration, it'll render only once.

See https://github.com/rackt/react-redux/issues/263 (there's a jsbin link there)

In the case of a large app that receives pushed data, should the data structure live in the state tree?
If push was happening every second, we would end up with thousands of state history right? I do understand that the reducers are just changing a small part of the tree.

A side effect of this would be that the devtools screen would have lots of dom elements and would soon slow down the browser. It comes to a point where it's unusable.

Is this a correct assumption?

@jorgecarmona That only affects Dev Tools, which aren't used in production. Dev Tools, like most other debugging or development tooling, aren't designed to be performant. The concern here is real world performance.

@timdorr Agreed.

So back to real world performance. Should the data that is streaming into the app every second be accumulated in the state tree? Does this approach scale?

If you're just appending to an array and it never becomes sparse, then it should be. You might run into some O(n) performance, but you can also solve that via checkpointing and breaking things into sub-arrays. I don't think that's going to be your bottleneck as much as the frontend DOM maintenance would be.

@timdorr I will do some tests with those recommendations.

Thanks for taking the time to answer!

@gaearon @timdorr react-ignore helps to skip some reducer subtrees for some actions so we have minimal update in our state tree.
But how about all those components that have been subscribed to state changes.
They will get notified even though if the state associated to the component haven't been changed.

Is there away to avoid it ?

react-reducer helps to skip some reducer subtrees for some actions so we have minimal update in our state tree.

Not sure what you are referring to. I’m not aware of such project. Did you mean https://github.com/omnidan/redux-ignore?

But how about all those components that have been subscribed to state changes.

Use https://github.com/reactjs/react-redux which takes care of this. It lets you specify specific parts of the state you care about, and takes care to bail out of updating React components when the relevant parts have not changed.

I am closing this thread because the original discussion has quieted down.

@gaearon

Use https://github.com/reactjs/react-redux which takes care of this. It lets you specify specific parts of the state you care about, and takes care to bail out of updating React components when the relevant parts have not changed.

I read the following in the doc.

mapStateToProps(state, [ownProps]): stateProps: If specified, the component will subscribe to Redux store updates. Any time it updates, mapStateToProps will be called. Its result must be a plain object*, and it will be merged into the component’s props

Just wanted to fully understand that what's going on under the hood here, So if the Redux store gets updated but one specific component state hasn't changed, Redux won't trigger the forceUpdate() method for that component.

If the answer is yes, you have made my day :)

React Redux does not use forceUpdate() at all. It’s much more careful :-)
Yep, it won't be causing unnecessary updates.

More specifically: the wrapper component generated by React-Redux's connect() function does a several checks to try to minimize the number of times your actual component has to re-render. This includes a default implementation of shouldComponentUpdate, and doing shallow equality checks on the props going into your component (including what's returned from mapStateToProps). So yes, as a general rule a connected component will only re-render when the values it's extracting from state have changed.

:+1: Sweet.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ramakay picture ramakay  ·  3Comments

amorphius picture amorphius  ·  3Comments

caojinli picture caojinli  ·  3Comments

benoneal picture benoneal  ·  3Comments

CellOcean picture CellOcean  ·  3Comments