Understanding React Context

Traditionally, you pass information from a parent component to child component via props in React. But data passing through this props drilling can make your code verbose and inconvenient if you have more components in the middle, or if many components in your app need the same information.

For solving this, in V.16.3 React introduced Context and it helps you to share data to any component with out passing it explicitly through props. This context can be used to manage the global data such as user info, theme preferences, settings, and much more.

In this post, you’ll learn how to use the context in React.

Creating the Context

According to the React docs, React Context provides a way to pass data through the component tree from parent to child components, without having to pass props down manually at each level.

To create a Context, you must import createContext function from React and intialize it with default value, if nothing to initialize you can specify null. The default value will use if there is no matching context provider in the tree above the component that reads context. The default value is static and never changes with time.

// ThemeContext.js
import { createContext } from "react";

const ThemeContext = createContext({
  theme: "light",
  toggleTheme: () => {},
});

The createContext function will return a context object and it does not hold any information. It is a representation of context which other components read or provide. Typically, someContext.Provider in components will provide the context values and you can access these values by using useContext(someContext) in other components.

Providing the Context

By using someContext.Provider you can wrap the components and pass the context value to the components inside. By passing context value through this Provider, all the components inside the Provider can access the value, no matter how deep the components are. You can pass values with any type through the provider as shown below.

// ThemeContextProvider.js
import { ThemeContext } from "./ThemeContext";

function ThemeContextProvider({children}) {
  const [theme, setTheme] = useState("light");
  // ...
  const cnxtValue = {
    theme: theme,
    toggleTheme: handleToggleTheme,
  };

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

Consuming the Context

Consuming the context in the component can be acheived with React hook useContext. Through the useContext you can read and subscribe to context from your component.

You can call useContext in the top your component and pass the context that you have created previously with createContext. It will provide the value passed through by someContext.Provider. If there is no values available, it will return the defaultValue intialized by the createContext. React will automaticlaly re-render all the components that use a particular context if any value is changed in the context.

// Page.js
import { useContext } from "react";

function Page() {
  const { toggleTheme, theme } = useContext(ThemeContext);
  // ...
}

Where to use Context

The main idea of using Context is to access global data and re-render the components when the global data is changed. It can help you to avoid props drilling and make your code more effective.

Below are the some of the usecases for React Cotext:

  • global state
  • application configuration
  • theme settings
  • user information
  • authentication details
  • routing

How to use Context

Below you can find a simple application have a theme switching button to show how we can use React Context. Here we are setting the default values with createContext, passing the theme data to components by using Provider, and finally accessing the data in the components by using useContext.

You can view the full code on GitHub.

ThemeContext.js

import { createContext } from "react";

export const ThemeContext = createContext({
  theme: "light",
  toggleTheme: () => {},
});

ThemeContextProvider.js

import { useState } from "react";
import { ThemeContext } from "./ThemeContext";

export default function ThemeContextProvider({ children }) {
  const [theme, setTheme] = useState("light");

  function handleToggleTheme() {
    if (theme === "light") {
      setTheme("dark");
    } else {
      setTheme("light");
    }
  }

  const cnxtValue = {
    theme: theme,
    toggleTheme: handleToggleTheme,
  };

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

Page.js

import { useContext } from "react"; 
import { ThemeContext } from "./ThemeContext.js";

export default function Page() {
  const { toggleTheme, theme } = useContext(ThemeContext);

  return (
    <div id="app" className={theme}>
      <header>
        <h1>Context Demo</h1>
        <button onClick={toggleTheme}>Toggle Theme</button>
      </header>

      <article>
        <h2>Context in React</h2>
        <p>
          This example will show you to use Context!
        </p>
      </article>
    </div>
  );
}

App.js

import ThemeContextProvider from "./components/theme/ThemeContextProvider";
import Page from "./components/theme/Page.jsx";

function App() {
  return (
    <ThemeContextProvider>
      <Page />
    </ThemeContextProvider>
  );
}

export default App;

In the above example you can see, when we are clicking the theme toggle button the state is changing and page component is re-rendering immediately to switch the theme.

The React context is stateless by default and it doesn’t provide any dedicated method to update the state. For updating the state you can use hooke like useState or useReducer. In the above app, the components use useState to switch the theme between light and dark.

Summary

  • By using React Context you can avoid props drilling and manage global state
  • Using the context requires three steps: Creating, providing, and cosuming context.
  • You can handle the state with hooks like useState or useReducer in context.
References

React Doc, React Doc, Telerik, React legacy, Log Rocket, Dmitri Pavlutin

Read More