Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redux Performance with Large Store and frequent updates #1303

Closed
landabaso opened this issue Jan 28, 2016 · 19 comments
Closed

Redux Performance with Large Store and frequent updates #1303

landabaso opened this issue Jan 28, 2016 · 19 comments

Comments

@landabaso
Copy link

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

@sompylasar
Copy link

This comment and the whole discussion may help: #1287 (comment)

@landabaso
Copy link
Author

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!

@gaearon
Copy link
Contributor

gaearon commented Jan 28, 2016

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

@landabaso
Copy link
Author

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!

@timdorr
Copy link
Member

timdorr commented Jan 28, 2016

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.

@elado
Copy link

elado commented Jan 29, 2016

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 reduxjs/react-redux#263 (there's a jsbin link there)

@jorgecarmona
Copy link

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?

@timdorr
Copy link
Member

timdorr commented Feb 2, 2016

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

@jorgecarmona
Copy link

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

@timdorr
Copy link
Member

timdorr commented Feb 2, 2016

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.

@jorgecarmona
Copy link

@timdorr I will do some tests with those recommendations.

Thanks for taking the time to answer!

@pedramphp
Copy link

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

@gaearon
Copy link
Contributor

gaearon commented Feb 25, 2016

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.

@gaearon
Copy link
Contributor

gaearon commented Feb 25, 2016

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

@gaearon gaearon closed this as completed Feb 25, 2016
@pedramphp
Copy link

@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 :)

@gaearon
Copy link
Contributor

gaearon commented Feb 25, 2016

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

@markerikson
Copy link
Contributor

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.

@pedramphp
Copy link

👍 Sweet.

@vytenisu
Copy link

On extremely complex projects performance can drastically be improved by throttling notifications between Redux and React and also marking certain actions as "passive" (changing state but not causing render). Have a look at this:

https://www.npmjs.com/package/redux-notification-enhancer

It is important to note that it is breaking default Redux flow a bit and can increase project complexity - use with care. Most important considerations are mentioned in readme.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants