DEV Community

Cover image for Optimise rendering of children tree subscribed to Context API
calebdeji
calebdeji

Posted on

Optimise rendering of children tree subscribed to Context API

A few months ago, I got to develop a web application that required optimal state management in the sense that each component in the application needs to re-render only when changes are made to state data bound to the component. Thinking of the perfect architecture that seemed to fit the project, I came up with an architectural pattern which followed the rule stating that the app should be contained in a global state manager ( that holds data that rarely change such as authentication data) and also each route should have its own state manager ( Context API), hence to prevent unnecessary re-render whenever there's a change in data other routes.

This article assumes that you as a reader have some experience with state management using React. If you are new to React state management, I recommend that you check out this article by Kent Dodds before proceeding.

Notice how each route encompasses a state manager that contains the route component. Putting this kind of structure in place is particularly important for the sake of readability, scalability, and maintainability. It is easy to handle errors in each route’s state manager, and separation of concern actually makes development easy.

I completed the project and it seemed perfect but then I noticed that each component subscribed to each route state manager re-rendered whenever changes were made to data held by the manager. The following is an example of what I meant

This is cool, so what is the problem?

It works pretty well. However, the issue is that for every update made in the context data, all components that subscribed to the context API re-renders. We really do not want each expensive component subscribed to a particular context manager to re-render every time we update the state even though the data attached to the component didn't change. What shall we do to prevent this?

What's the solution to the problem then?

If we were using class-based components, we could easily prevent re-renders with the shouldComponentUpdate method or employ the use of pure React components that are made for issues like this but these are functional components. Don't be scared, we have a savior called useMemo. useMemo?? Ah Yes.

useMemo is a built-in React hook that returns a memoized value. It allows you recompute expensive functions only when one of the dependencies has changed

Note the following:

useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render. Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in the useEffect hook, not useMemo. You can read more on useMemo here.

How do we use useMemo in this kind of scenario? The answer to the “how” is shown in the code snippet below.

Note: Our components would still re-execute, but React wouldn't re-render the child tree if all useMemo inputs were the same.

This pattern helps us to solve the problem we have at hand - any child of each component that subscribed to a particular context API only re-renders when the necessary data attached to its useMemo changes.

Context API shouldn't be used as a global state manager that holds data that changes frequently for performance's sake, you can use Redux for that. Thanks for reading.

Feel free to drop suggestions and questions in the comments section below.

Discussion (2)

Collapse
medelba profile image
MedElba

Awesome post! Really very helpful!
I have my context provider wrapping a tab navigator, so every time i switch tab, the context sees the children changing and rerenders, causing all the screen to rerender!
Now I've adopted your solution simply by adding useMemo in the return function of the context and giving as input the state of the context, since it's the only value that context handles! It's working for now ahahah but I only tried for a few hours.. What you think about this approach? Do you think that even with this kind of optimization another state manager like Redux is far better?

Collapse
calebdeji profile image
calebdeji Author

I think it is good. Redux too is okay, but you need to be careful with what you extract from useSelector hook in order to prevent redundant rendering.