Telerik blogs

Learn how and when to use React Context to manage state and data between React components.

Context, in React, is a powerful tool that allows us to share data between components without having to pass props down every level of a component tree. We’ve written content before on understanding how the React Context API works. In this article, we’ll further explore what React Context is and how it works, and focus on specific use cases where React Context can best be used.

What is Context?

When managing data between parent and child components, React gives us the ability to use something known as props to pass data down from parent to child. Props can only flow in one direction, from parent components to child components (and further down). When state changes occur on parent elements, React will re-render components that depend on those values.

Context, in React, is a way to pass data down through a component tree without having to pass props down through every level. This can be very helpful since this allows us to share data between components that are not directly related to each other (e.g., between sibling components).

How Does Context Work?

React Context works by creating a context object that holds the data we want to share between components. We create this context object with the createContext() function:

import { createContext } from "react";

const MyContext = createContext();

Once we’ve created the context object, we can use it in our components by first wrapping the components in a Context Provider component:

<MyContext.Provider value={/* some value */}>
  {/* Your components */}
</MyContext.Provider>

Any component that is a child of the Provider component can then access the data in the context using the useContext() hook:

import { useContext } from "react";
import MyContext from "./MyContext";

function MyComponent() {
  const value = useContext(MyContext);
  // ...
}

When Should We Use React Context?

React Context is a powerful tool, but it’s not always the right tool for the job. Here are a few scenarios where we might find it helpful to use React Context to share data within an application.

Theme Management

Theme management can refer to the process of managing and customizing the appearance of the user interface of the application. Using React Context is great for this use case since it allows us to easily share the same theme across an entire app, regardless of how many components are being used. This makes it easy to keep all components in sync with the same theme without having to manually update each one separately.

When building the theme context, we can store the theme data (such as color schemes, fonts, etc.) in a context object.

import React, { createContext, useState } from "react";

const theme = {
  foreground: "#000000",
  background: "#eeeeee",
  primaryColor: "#4285f4",
  secondaryColor: "#34a853",
  tertiaryColor: "#fbbc05",
  fontFamily: "Arial, sans-serif",
  fontSize: "16px",
  // ...
};

export const ThemeContext = createContext();

export const App = () => {
  return (
    <ThemeContext.Provider value={{ theme }}>
      {/* child components */}
    </ThemeContext.Provider>
  );
};

In the example above, any component in our app rendered as a child to the parent component has access to the theme object and toggleTheme() function available in Context.

import React, { useContext } from "react";
import { ThemeContext } from "./App";

const Button = () => {
  // Access the theme object from Context
  const { theme } = useContext(ThemeContext);

  return (
    <button
      style={{
        backgroundColor: themes[theme].background,
        color: themes[theme].foreground,
      }}
    >
      Click Me
    </button>
  );
};

User Authentication

Another scenario where React Context can be useful is for managing user authentication. For example, when a user logs in to an application, we might want to keep track of their user information (such as name, email, etc.) and authentication status (whether they are logged in or not) so that we can display personalized content and restrict access to certain features from the UI.

In this case, we can create an authentication Context object to store the user information and authentication status. We can use the Context to provide this information to any components that need it, such as a navbar or a protected route.

Here’s a simple example of creating an authentication Context where Context contains information about the user state, whether the user is authenticated, and login/logout functions that can be used in any component of our app to update the user information and authentication status.

import React, { createContext, useState } from "react";

export const AuthContext = createContext();

export const App = ({ children }) => {
  const [user, setUser] = useState(null);
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const login = (userData) => {
    // Authenticate user and set user information and authentication status
    setUser(userData);
    setIsAuthenticated(true);
  };

  const logout = () => {
    // Remove user information and set authentication status to false
    setUser(null);
    setIsAuthenticated(false);
  };

  return (
    <AuthContext.Provider value={{ user, isAuthenticated, login, logout }}>
      {/* child components */}
    </AuthContext.Provider>
  );
};

In child components, we can access the user information and authentication status in any component using the useContext() hook.

import React, { useContext } from "react";
import { AuthContext } from "./AuthProvider";

const Navbar = () => {
  // Access the user information and authentication status from Context
  const { user, isAuthenticated } = useContext(AuthContext);

  return (
    <nav>
      <ul>
        <li>{user ? user.name : "Guest"}</li>
        <li>{isAuthenticated ? "Logged In" : "Logged Out"}</li>
      </ul>
    </nav>
  );
};

Localization

Another scenario where React Context can be useful is for managing localization, which involves displaying content in different languages based on the user’s preferred language or location. By using React Context to store the user’s selected language, we can easily provide this information to any components that need it.

Here’s a rough example of creating a localization Context where the Context object contains the user’s selected language and a function to update the language.

import React, { createContext, useState } from "react";

export const LocalizationContext = createContext();

export const App = ({ children }) => {
  const [language, setLanguage] = useState("en");

  const changeLanguage = (newLanguage) => {
    setLanguage(newLanguage);
  };

  return (
    <LocalizationContext.Provider value={{ language, changeLanguage }}>
      {/* child components */}
    </LocalizationContext.Provider>
  );
};

In child components, we can access the user’s selected language using the useContext() hook.

import React, { useContext } from "react";
import { LocalizationContext } from "./LocalizationProvider";

const greetings = {
  en: "Hello",
  es: "Hola",
  fr: "Bonjour",
};

const Greeting = () => {
  const { language } = useContext(LocalizationContext);
  return <div>{greetings[language]}</div>;
};

By using Context to manage localization, we can easily update the language in one place and have all components that rely on the selected language automatically updated.

Keep in mind that the code examples we’ve shared above are client-side implementations. Particularly for localization and user authentication, interacting with a server will most likely be necessary to fully implement these features in a production environment.

Outside of the examples we’ve discussed above, React Context can sometimes help manage the global state of an application, caching, handling complex routing, and a lot more.

Conclusion

Context is a powerful tool that can be used to simplify and optimize the development experience when working with React and is especially useful when data needs to be shared across multiple components that are not directly related to each other.

When using Context, it’s important to keep in mind that it is best used when necessary. If the data only needs to be shared between parent and child components, using props is often the better choice.


About the Author

Hassan Djirdeh

Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.

Related Posts

Comments

Comments are disabled in preview mode.