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
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?
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?? 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
useMemoruns during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in the
useMemo. You can read more on
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
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.