Skip to content
Advertisement

Does redux evaluate all listeners to the store on any update to the store?

From what I can tell, redux will notify all subscribers to the store when anything in the store changes no matter if it’s a subscription to a deeply nested leaf or a subscription to the top level of the state.

In an application where you follow the guiding principle:

many individual components should be connected to the store instead of just a few… [docs]

You could end up with lots of listeners and potentially performance issues?

Disclaimer: I understand that the selector functions will only cause a re-render if the result of the selector function changes. I understand that just because the listener function is evaluated, doesn’t mean the subscribing component will re-render. I understand that evaluating a selector function is comparatively cheap to a react component rendering.

However, I just would like to confirm that this is indeed how redux works?

e.g. given the following example listener

const result = useSelector(state => state.a.b.c.d.e.f.g.h.i.j.k)

if we update some other value down some other path, not relevant to the above listener e.g.

const exampleReducer = (state) => {
   return { ...state, asdf: 'asdf' }
}

From my understanding, all listeners, including the example above, will be invoked.

For context, my actual use case is I’m using https://easy-peasy.now.sh/ which is built on redux. To be clear, I don’t have any current performance issues in production related to binding too many listeners. However, each time I attach a listener via the useStoreState hook, I’m wondering whether I should minimize binding yet another listener to the store.

Also if you’re curious, inspired by this thinking, I implemented a state tree which only notifies the relevant listeners.

Perhaps this is a premature optimization for a state library… but if so why? Is there an assumption that applications using redux will have simple and fast selectors and that the application bottleneck will be elsewhere?

Advertisement

Answer

I’m a Redux maintainer and author of React-Redux v7. The other couple answers are actually pretty good, but I wanted to provide some additional info.

Yes, the Redux store will always run all store.subscribe() listener callbacks after every dispatched action. However, that does not mean that all React components will re-render. Per your useSelector(state => state.a.b.c.d) example, useSelector will compare the current selector result with the previous selector result, and only force this component to re-render if the value has changed.

There’s multiple reasons why we suggest to “connect more components to read from Redux”:

  • Each component will be reading a smaller scoped value from the Redux store, because there’s less overall data that it cares about
  • This means that fewer components will be forced to re-render after a given action because there’s less of a chance that this specific piece of data was actually updated
  • Conversely, it means that you don’t have cases where one large parent component is reading many values at once, is always forced to re-render, and thus always causes all of its children to re-render as well.

So, it’s not the number of listener callbacks that’s really the issue – it’s how much work those listener callbacks do, and how many React components are forced to re-render as a result. Overall, our performance tests have shown that having more listeners reading less individual data results in fewer React components being re-rendered, and the cost of more listeners is noticeably less than the cost of more React components rendering.

For more info, you should read my posts on how both React and React-Redux work, which these topics in extensive detail:

You may also want to read the Github issue that discussed the development of React-Redux v7, where we dropped the attempt to use React Context for state propagation in v6 because it wasn’t sufficiently performant enough, and returned to using direct store subscriptions in components in v7.

But yes, you’re worrying too much about performance ahead of time. There’s a lot of nuances to how both React and React-Redux behave. You should actually benchmark your own app in a production setting to see if you actually have any meaningful performance issues, then optimize that as appropriate.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement