I used to think of Redux as a straightforward state management tool: dispatch an action, reducer updates the state, and components re-render. That mental model served me well — until our app grew to handle a surge of customer data and real-time collaboration features. Suddenly, the performance took a nosedive.
Diving deep into the problems, we traced several bottlenecks. One surprising finding was in how Redux’s subscription works. Each time we used useSelector
, we essentially told Redux, „Hey, keep me updated on any change in the store“. This was significant because as our app scaled, the number of subscriptions and frequency of updates led to a cascade effect of executing dozens of selectors.
After looking closer into them, we uncovered a few culprits:
- We under-utilized memorization; Some selectors repeatedly performed costly operations by iterating over extensive collections to filter and combine data.
- For parametrized selectors, we didn’t use memorization properly (nuances of
reselect
vs.re-reselect
). - Array-based approach for collections wasn't optimal for lookups (e.g., finding objects by ID).
- Over-fetching data, and not cleaning up the state after changing contexts resulted in bloating the store with unused data.
- Selectors were running even for off-screen elements.
Once we knew types of our issues, it was relatively easy to come up with solutions to improve the situation. Here is the list of the main changes we've introduced:
- More extensive use of
reselect
andre-reselect
to avoid redundant computations and to account for selectors returning new objects by a reference. - Refactored store away from arrays, adopting a key/value structure for faster lookups by ID.
- For real-time updates, we throttled them and combined into batched updates to avoid dispatching sequential, and atomic updates.
- Started fetching data more conservatively to avoid storing data our users don’t need.
- Last but not least, we hid off-screen elements to reduce active subscriptions.
Lessons
Performance optimization is a complex puzzle, and solutions often depend on multiple factors. In our case, taking a step back to gain a holistic understanding of our tools, system, and usage patterns was the key to identifying and implementing effective optimizations.
I hope you find it useful! 🙂
Top comments (0)