DEV Community

Amarachi Nwokocha
Amarachi Nwokocha

Posted on

Understanding useContext in React

Passing down data in React components has been one of the major challenges faced by React developers since the introduction of functional components. Every component needs to use data, either wholly or in parts. While useState has been our go-to for holding data locally, its limitations make it difficult to pass down data efficiently. In the past, we relied on props, leading to issues like prop drilling and cumbersome state management, making components error-prone and harder to maintain. This is where useContext comes in.

In this article, we will discuss:

  • What is useContext?
  • How to set up Context in React
  • How to use useContext in components
  • When to use useContext
  • Common mistakes and best practices

On that note, let's dive into it!

What is useContext?

Simply put, useContext is a way of sharing data across a React application without having to pass it manually through props. It works with React.createContext to pass down data to components.

Imagine we have a large farm, which undoubtedly leads to a large storehouse. This storehouse has a farmhand who keeps track of the tools and delivers them where needed. React Context is the storehouse that holds all the data or “tools,” and useContext is our trusted farmhand that fetches these tools from storage and delivers them where needed.

useContext was introduced in React version 16.8. It leverages the Context API, which allows a user to store data in a global state. How does it do this? React.createContext is used to create a Context object (which includes Provider and Consumer components), and useContext is a hook that allows you to consume the context value from React.createContext. It is important to note that the useContext hook can only be used in functional components.

How to Set Up Context in a React Project

Setting up Context in a React application is straightforward. Follow these steps:

  1. Create a context file.
  2. Create the context using the React Context API.
  3. Provide the context value.
  4. Wrap the app in the provider.
  5. Consume the context in the child component.

See? Very easy! Now, let's put these steps into code.

Step 1: Create a Context File

Some developers prefer to create a context folder and store all context files inside it. This is especially useful when dealing with multiple contexts in a project.

Step 2: Create the Context using the React Context API

import React, { createContext, useContext, useState } from "react";
const DataContext = createContext();
Enter fullscreen mode Exit fullscreen mode

Above, we are:

  • Importing useState (to locally hold the data).
  • Using createContext to create the context.
  • Importing useContext so we can send the data to the components.

Step 3: Provide the Context Value

In our project, the context value will be data fetched from an API and passed down to components. This ensures that all components have access to the fetched data.

export const DataProvider = ({ children }) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  return (
    <DataContext.Provider value={{ data, loading, error }}>
      {children}
    </DataContext.Provider>
  );
};

// Custom hook to use the DataContext
export const useData = () => useContext(DataContext);
Enter fullscreen mode Exit fullscreen mode

Above, we:

  • Created the DataProvider component, which accepts children as an argument.
  • Initialized the data, loading, and error states.
  • Used DataContext.Provider to wrap the children, providing data to all components in the tree.
  • Created a useData hook to make accessing context data easier.

Now, let's fetch our data using useEffect:

useEffect(() => {
  setLoading(true);
  axios
    .get("https://jsonplaceholder.typicode.com/users")
    .then((response) => {
      setData(response.data);
      setLoading(false);
    })
    .catch((error) => {
      setError(error);
      setLoading(false);
      console.log(error);
    });
}, []);
Enter fullscreen mode Exit fullscreen mode

Step 4: Wrap the App in the Provider

To ensure our data is accessible to all components, we wrap our App component with DataProvider:

const App = () => {
  return (
    <DataProvider>
      <Components />
    </DataProvider>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Step 5: Consume the Context in the Child Component

import DataContext from "../context/DataContext";
import { useContext } from "react";

const { data, error, loading } = useContext(DataContext);
Enter fullscreen mode Exit fullscreen mode

Now, all values (data, error, loading) can be used in any component!


When to Use useContext

There are various scenarios where useContext is useful:

  1. Avoiding Prop Drilling – When sharing data across multiple levels of components, useContext can provide direct access to the data without passing props manually.
  2. Managing Theme or UI Preferences – For apps with cross-functional UI preferences such as light/dark mode, useContext is a great way to manage state globally.
  3. Centralizing API Data or Configurations – When API data needs to be used across various components, useContext is an efficient way to share this globally.
  4. Simplifying Component Composition – When multiple components use the same logic or shared values (e.g., modals, notifications, form steps), useContext provides a cleaner way to manage them.

Common Mistakes and Best Practices When Using useContext

  1. Using Context for Everything – Overusing useContext when props would be a better choice. Instead, use useContext for truly shared or global state.
  2. Using useContext in a Component Not Wrapped in a Provider – Ensure all components that consume context are wrapped in <Context.Provider>.
  3. Using Multiple Contexts Inefficiently – Avoid deeply nesting multiple contexts. Instead, use a custom provider component to combine them efficiently.

Conclusion

In this article, we covered:

  • The concept and importance of useContext in React.
  • How to set up and use useContext effectively.
  • Common pitfalls to avoid and best practices to follow.

useContext is a powerful tool that simplifies state management in React applications. It eliminates unnecessary prop drilling and makes components cleaner and more maintainable. However, it's essential to use it wisely and only when necessary.

To learn more, check out the official React documentation on Context.

Happy coding! 🚀

Top comments (0)