How did I re-render: Sharing State through React-Context

Bhavya Saggi
4 min readJan 5, 2022

--

It is a common pattern to “lift the state” up the component tree to a “Least-Common Ancestor” React Component if a state is shared amongst multiple sibling Components.
But, the child component that should not be functionally dependent upon the ‘lifted’ state will re-render when the state changes.

React Component Tree with interacting Sibling Components

Another concern with a “lifted state” is to pass the state down the component tree recursively through component props. (Commonly known as ‘prop-drilling’).

React Context API is an amazing utility that is either overlooked or misused, in favor of a global state-management library that utilizes a huge store, which is available across the entire application.
Though using additional libraries is not wrong, and solves the problem of manually passing down the state to Child Components via props, usage of these global state-management libraries often leads to complications like:

  1. Usage of Higher-Order Components to provide access to the Store.
  2. Maintaining State Hierarchy.
  3. Tracing interactions between reducers, actions, and dispatch calls.

React Applications should be designed without using a global store in favor of the Component’s local state. Meanwhile React Context could be used to share global data like UI preferences, and expose global methods like Authentications.

React Context is not a State-Management Tool. It is a transport mechanism.

Let’s see the benefits of React-Context through a generic use case, an “Off-Canvas Sidebar Menu”. The solutions are to be assessed over prop-drilling avoidance and reduction in unnecessary re-renders.

Off-canvas Sidebar Menu

An off-canvas Sidebar Menu is a complete Component of its own, whose visibility is controlled by a button that exists outside of the Component.

An off-canvas Sidebar Menu or Hamburger Menu
An off-canvas Sidebar Menu or Hamburger Menu

Therefore, the visibility state needs to be ‘lifted’ up the component tree. To understand the scenario better, let's start designing it step by step.

Please view the console in each example to understand the re-rendering Impact of each action.

Menu Design with a Lifted-State

In a mock-up of the aforementioned scenario, the App Component acts as the root component of the page with Nav & Aside Components as its children.

The show the state is lifted to App and is then passed down to the components via props. Upon clicking the menu button the console prints the following:

>> Rendering NAV
>> Rendering ASIDE
>> Rendering APP

This solves none of the issues. The Entire Component Tree is re-rendering and props need to be manually passed down.

Menu Design by React Context

The component Tree can be redesigned by adding a React Context and passing the state variables through it. Upon clicking the menu button and checking the console, it prints the following:

>> Rendering NAV
>> Rendering ASIDE
>> Rendering APP

Usage of React Context does solve ‘prop-drilling’, but the entire Component Tree still suffers from excessive re-rendering.

Menu Design by Stateful Higher-Order Context

The visibility-state logic does not pertain to App Component, so moving it entirely into the Context Provider might save App from re-rendering.

Upon checking the console after pressing the menu button, it prints the following:

>> Rendering NAV
>> Rendering ASIDE

Usage of stateful React Context saves the Parent Component from re-rendering and avoids prop-drilling, but there is still unnecessary re-rendering of Nav Component.

Menu Design by Stateful Higher-Order Dual-Context

Upon deeper inspection, we realize that the Nav component does not require the show state, it only needs setShow to change the state.

Redesigning the Component Tree by using different Contexts for the state variables and state-update functions, in the Higher-Order Component for Context.

Upon checking the console after pressing the menu button, it prints the following:

>> Rendering ASIDE

Usage of stateful React Context prevents unnecessary re-rendering and avoids prop-drilling.

Both issues of prop-drilling & unnecessary re-rendering of App and Nav Components are solved via using “Stateful Higher-Order Dual-Context”.

Conclusion

From the above activity, it could be concluded that while React Context is good at solving prop-drilling, careful designing is required to prevent unnecessary re-renders. The appropriate usage of React Context demands the following:

  1. Always design a Higher-Order-Component for the Context-Providers.
  2. Always keep state variables and state-update functions in separate Contexts.
  3. Always pass the context-consuming Components either as a child component or as props, to the Higher-Order-Component for the Context-Providers.

The reasons for aforementioned conditions are:

— The “Stateful Higher-Order Dual-Context” paradigm consumes Components via children prop. This prevents re-rendering of child Components, even if Parent Component re-renders.
Read more on https://kentcdodds.com/blog/optimize-react-re-renders

— State-update Functions do not change their reference between renders. So, keeping it separate than state variable benefits the Components that only needs to consume the state-update functions.

Read more about the usage of React Context on:
https://kentcdodds.com/blog/how-to-use-react-context-effectively
https://kentcdodds.com/blog/how-to-optimize-your-context-value

--

--