Pitfalls of overusing React Context
Tags: react, javascript, tutorial
Published: 2020-02-07 18:14:22

---
title: Pitfalls of overusing React Context
published: true
date: 2020-02-04 18:30:57 UTC
tags: react,javascript,tutorial
canonical_url: https://blog.logrocket.com/pitfalls-of-overusing-react-context/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/tf5ymw7v42phe112yv0x.png
---
**Written by [Ibrahima Ndaw](https://blog.logrocket.com/author/ibrahimandaw/)**✏️

For the most part, React and state go hand-in-hand. As your React app grows, it becomes more and more crucial to manage the state.

With [React 16.8 and the introduction of hooks](https://dev.to/bnevilleoneill/introducing-react-16-8-featuring-official-support-for-hooks-lbd), the React Context API has improved markedly. Now we can combine it with hooks to mimic `react-redux`; some folks even use it to manage their entire application state. However, React Context has some pitfalls and overusing it can lead to performance issues.

In this tutorial, we’ll review the potential consequences of overusing React Context and discuss how to use it effectively in your next React project.

### What is React Context?

React Context provides a way to share data (state) in your app without passing down props on every component. It enables you to consume the data held in the context through providers and consumers without prop drilling.

```jsx
const CounterContext = React.createContext();

const CounterProvider = ({ children }) => {
  const [count, setCount] = React.useState(0);

  const increment = () => setCount(counter => counter + 1);
  const decrement = () => setCount(counter => counter - 1);
  return (
    <CounterContext.Provider value={{ count, increment, decrement }}>
      {children}
    </CounterContext.Provider>
  );
};

const IncrementCounter = () => {
  const { increment } = React.useContext(CounterContext);
  return <button onClick={increment}> Increment</button>;
};

const DecrementCounter = () => {
  const { decrement } = React.useContext(CounterContext);
  return <button onClick={decrement}> Decrement</button>;
};

const ShowResult = () => {
  const { count } = React.useContext(CounterContext);
  return <h1>{count}</h1>;
};

const App = () => (
  <CounterProvider>
    <ShowResult />
    <IncrementCounter />
    <DecrementCounter />
  </CounterProvider>
);
```

Note that I intentionally split `IncrementCounter` and `DecrementCounter` into two components. This will help me more clearly demonstrate the issues associated with React Context.

As you can see, we have a very simple context. It contains two functions, `increment` and `decrement`, which handle the calculation and the result of the counter. Then, we pull data from each component and display it on the `App` component. Nothing fancy, just your typical React app.

![Gordon Ramsay Asking, "What Is the Problem?"](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/02/gordon-ramsay-what-is-the-problem.gif?resize=500%2C338&ssl=1)

From this perspective, you may be wondering what’s the problem with using React Context? For such a simple app, managing the state is easy. However, as your app grows more complex, React Context can quickly become a developer’s nightmare.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

### Pros and cons of using React Context

Although React Context is simple to implement and great for certain types of apps, it’s built in such a way that every time the value of the context changes, the component consumer rerenders.

So far, this hasn’t been a problem for our app because if the component doesn’t rerender whenever the value of the context changes, it will never get the updated value. However, the rerendering will not be limited to the component consumer; all components related to the context will rerender.

To see it in action, let’s update our example.

```jsx
const CounterContext = React.createContext();

const CounterProvider = ({ children }) => {
  const [count, setCount] = React.useState(0);
  const [hello, setHello] = React.useState("Hello world");

  const increment = () => setCount(counter => counter + 1);
  const decrement = () => setCount(counter => counter - 1);

  const value = {
    count,
    increment,
    decrement,
    hello
  };

  return (
    <CounterContext.Provider value={value}>{children}</CounterContext.Provider>
  );
};

const SayHello = () => {
  const { hello } = React.useContext(CounterContext);
  console.log("[SayHello] is running");
  return <h1>{hello}</h1>;
};

const IncrementCounter = () => {
  const { increment } = React.useContext(CounterContext);
  console.log("[IncrementCounter] is running");
  return <button onClick={increment}> Increment</button>;
};

const DecrementCounter = () => {
  console.log("[DecrementCounter] is running");
  const { decrement } = React.useContext(CounterContext);
  return <button onClick={decrement}> Decrement</button>;
};

const ShowResult = () => {
  console.log("[ShowResult] is running");
  const { count } = React.useContext(CounterContext);
  return <h1>{count}</h1>;
};

const App = () => (
  <CounterProvider>
    <SayHello />
    <ShowResult />
    <IncrementCounter />
    <DecrementCounter />
  </CounterProvider>
);
```

I added a new component, `SayHello`, which displays a message from the context. We’ll also log a message whenever these components render or rerender. That way, we can see whether the change affects all components.

```jsx
// Result of the console
 [SayHello] is running
 [ShowResult] is running
 [IncrementCounter] is running
 [DecrementCounter] is running
```

When the page finishes loading, all messages will appear on the console. Still nothing to worry about so far.

Let’s click on the `increment` button to see what happens.

```jsx
// Result of the console
 [SayHello] is running
 [ShowResult] is running
 [IncrementCounter] is running
 [DecrementCounter] is running
```

As you can see, all the components rerender. Clicking on the `decrement` button has the same effect. Every time the value of the context changes, all components’ consumers will rerender.

You may still be wondering, who cares? Isn’t that just how React Context works?

![Joey From "Friends" Shrugging](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/02/friends-who-cares.gif?resize=192%2C231&ssl=1)

For such a tiny app, we don’t have to worry about the negative effects of using React Context. But in a larger project with frequent state changes, the tool creates more problems than it helps solve. A simple change would cause countless rerenders, which would eventually lead to significant performance issues.

So how can we avoid this performance-degrading rerendering?

### Prevent rerendering with `useMemo()`

Maybe memorization is the solution to our problem. Let’s update our code with `useMemo` to see if memorizing our value can help us avoid rerendering.

```jsx
const CounterContext = React.createContext();

const CounterProvider = ({ children }) => {
  const [count, setCount] = React.useState(0);
  const [hello, sayHello] = React.useState("Hello world");

  const increment = () => setCount(counter => counter + 1);
  const decrement = () => setCount(counter => counter - 1);

  const value = React.useMemo(
    () => ({
      count,
      increment,
      decrement,
      hello
    }),
    [count, hello]
  );

  return (
    <CounterContext.Provider value={value}>{children}</CounterContext.Provider>
  );
};

const SayHello = () => {
  const { hello } = React.useContext(CounterContext);
  console.log("[SayHello] is running");
  return <h1>{hello}</h1>;
};

const IncrementCounter = () => {
  const { increment } = React.useContext(CounterContext);
  console.log("[IncrementCounter] is running");
  return <button onClick={increment}> Increment</button>;
};

const DecrementCounter = () => {
  console.log("[DecrementCounter] is running");
  const { decrement } = React.useContext(CounterContext);
  return <button onClick={decrement}> Decrement</button>;
};

const ShowResult = () => {
  console.log("[ShowResult] is running");
  const { count } = React.useContext(CounterContext);
  return <h1>{count}</h1>;
};

const App = () => (
  <CounterProvider>
    <SayHello />
    <ShowResult />
    <IncrementCounter />
    <DecrementCounter />
  </CounterProvider>
);
```

Now let’s click on the `increment` button again to see if it works.

```jsx
<// Result of the console
 [SayHello] is running
 [ShowResult] is running
 [IncrementCounter] is running
 [DecrementCounter] is running
```

Unfortunately, we still encounter the same problem. All components’ consumers are rerendered whenever the value of our context changes.

![Michael Scott Making a Sad Face](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/02/michael-scott-sad.gif?resize=220%2C220&ssl=1)

If memorization doesn’t solve the problem, should we stop managing our state with React Context altogether?

### Should you use React Context?

Before you begin your project, you should determine how you want to manage your state. There are myriad solutions available, only one of which is React Context. To determine which tool is best for your app, ask yourself two questions:

1. When should you use it?
2. How do you plan to use it?

If your state is frequently updated, React Context may not be as effective or efficient as a tool like [React Redux](https://blog.logrocket.com/when-and-when-not-to-use-redux-41807f29a7fb/). But if you have static data that undergoes lower-frequency updates such as preferred language, time changes, location changes, and user authentication, passing down props with React Context may be the best option.

If you do choose to use React Context, try to split your large context into multiple contexts as much as possible and keep your state close to its component consumer. This will help you maximize the features and capabilities of React Context, which can be quite powerful in certain scenarios for simple apps.

So, [should you use React Context](https://dev.to/bnevilleoneill/use-hooks-context-not-react-redux-1j08)? The answer depends on when and how.

### Final thoughts

React Context is an excellent API for simple apps with infrequent state changes, but it can quickly devolve into a developer’s nightmare if you overuse it for more complex projects. Knowing how the tool works when building highly performant apps can help you determine whether it can be useful for managing states in your project. Despite its limitations when dealing with a high frequency of state changes, React Context is a very powerful state management solution when used correctly.

* * * 
## Full visibility into production React apps 

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, [try LogRocket.](https://www2.logrocket.com/react-performance-monitoring)

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/eq752g8qhbffxt3hp9t4.png)

[LogRocket](https://www2.logrocket.com/react-performance-monitoring) is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — [start monitoring for free.](https://www2.logrocket.com/react-performance-monitoring)
* * *

The post [Pitfalls of overusing React Context](https://blog.logrocket.com/pitfalls-of-overusing-react-context/) appeared first on [LogRocket Blog](https://blog.logrocket.com).