Optimizing React Context for Minimal Render Counts
October 24, 2022 · ⏱️ 3 min read
A bit about React's Context
React's Context lets developers pass down information through the component tree without using props, making your code base more readable. Without using the context and/or a state management library like Redux, your code base will likely suffer from prop drilling.
What's the catch?
This sounds too good to be true, right? Thankfully, you are reading this article. But for now, there's a catch with React's Context: Whenever the information in context changes, all the components that are consuming this context will re-render. This may be a deal-breaker for use cases where the information changes rapidly such as data dashboards, e-commerce etc. Your application may suffer from lagging and ultimately leading to a bad user experience. So what do you do? Remember, I mentioned that there are some state management libraries which you can use instead of React's Context such as Redux.
What is Redux?
The fundamental concept of Redux and React's Context are similar. They will both allow you to bypass the natural one-way data passing behavior and share some information without passing it down via props. But Redux will actually prevent extra re-renders since it uses a publisher-subscriber model and a global centralized store for state. The idea is that each component can subscribe to a sub-set of information in this global state and will re-render only when that specific part of information changes.
Why you shouldn't use Redux
Well, we found the solution then. Let's look no further and start using Redux. But no, once you get into Redux's documentation you will understand that it causes too much of an overhead to be integrated into your project compared to React's Context.
Because of how Redux is built, this complexity of integration is required by its nature but this is not relevant to you. You should only care about what you need to achieve, which is just passing some information down the component tree and not causing any extra re-renders.
Here are some disadvantages of Redux which makes it a no for me:
- Too much complexity for such a trivial solution.
- Need for extra plugins for async effects. Such as redux-thunk. See how it's documented here
- Bundle size is too large.
- Data processing logic is separated into so many parts because of how Redux works. State, Actions and Reducers model just makes things much more complex than they actually are
Unless you are building a data-driven dashboard where many information needs to be shared rapidly between components that are not in the same component sub-tree, Redux is definitely not worth it.
Let's make our context faster
As promised, there's a way where we can avoid the state management library hell and actually achieve this behavior using just Context and React Hooks.
I don't want to go into the implementation details here, so if you want to learn more about how this works please watch this amazing Youtube Video by Jack Herrington.
Here's the code snippet I took from Jack's repository:
Some key points are as follows:
-
Since any value stored via useState in the Context will cause a re-render, we take advantage of useRef.
-
Using publisher & subscriber model, we are able to let components know that there's a change specific parts of the data and only render the components which use that part.
-
Instead of maintaining this publisher & subscriber model ourselves, we take advantage of
useSyncExternalStore
which is a fairly new hook introduced in React 18.
And how you use it is as follows:
And there you go, now you have a fully functioning and efficient state management tool.