DEV Community

Tess Mueske
Tess Mueske

Posted on

useContext in React

useContext is a great way to pass props from a global state to all its children components in React. It eliminates 'prop drilling', which can be messy and frustrating, especially with deeply-nested components. This is an article on how to set up and use useContext to manage user state in React.

We'll start by creating the context itself, in a component with a name of your choosing. This is an example; here, I've named the component 'Context.js'.

Screenshot of components in a React app in Visual Studio Code, with a component named 'Context' highlighted

Let's import some basic items we'll need:

import React, { createContext, useState, useEffect } from "react";
Enter fullscreen mode Exit fullscreen mode

Then, at the top of our file, right below the import statements and before the function itself, we define the context.

const Context = createContext();
Enter fullscreen mode Exit fullscreen mode

^This line is very important^. It creates a context object, which supplies data to the children who need it. More on that later.

Then, just below the Context definition, we set up the start of our function, including its user state.

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState({
    email: "",
    username: "",
  });
Enter fullscreen mode Exit fullscreen mode

Let's break these steps down here piece by piece:

1) This is a function that takes in children, aka all of the components beneath this one, as a prop.

export const UserProvider = ({ children }) => {
Enter fullscreen mode Exit fullscreen mode

2) Then, we initialize state with a user object, comprised of email and username in this example.

const [user, setUser] = useState({ email: "", username: "" });
Enter fullscreen mode Exit fullscreen mode

Now we grab the current user as we might any other time: we make a call to a backend API with a fetch and populate user with the data returned from the backend.

useEffect(() => {
    fetch("/current-user", {
    })
      .then((response) => {
        if (!response.ok) {
          throw new Error("User not logged in");
        }
        return response.json();
      })
      .then((data) => setUser(data))
      .catch((err) => {
        console.error("Error fetching user:", err);
        setUser(null); 
      });
  }, []);
Enter fullscreen mode Exit fullscreen mode

Our backend route for /current-user is set up to set each user state with the email and username for the current user. It returns a response and we set the user state with that response.

Finally, we need a return statement.

return (
    <Context.Provider value={{ user, setUser }}>
      {children}
    </Context.Provider>
  );
};
Enter fullscreen mode Exit fullscreen mode

In this chunk of code, we are returning the provider (context) and setting the props of user and setUser to be passed down to all its children.

And that's it for our Context file! Let's put it together:

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

const UserContext = createContext();

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState({
    email: "",
    username: ""
  });

  useEffect(() => {
    fetch("/current-user", {
    })
      .then((response) => {
        if (!response.ok) {
          throw new Error("User not logged in");
        }
        return response.json();
      })
      .then((data) => setUser(data))
      .catch((err) => {
        console.error("Error fetching user:", err);
        setUser(null); 
      });
  }, []);

return (
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

export const useUserContext = () => useContext(UserContext);
Enter fullscreen mode Exit fullscreen mode

Don't forget to export!

The final step of setting up our Context is to wrap our entire app in it, usually in the index.js file:

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <BrowserRouter>
    <UserProvider>
      <App />
    </UserProvider>
  </BrowserRouter>
);
Enter fullscreen mode Exit fullscreen mode

With this implementation, your Context is wrapping all of App -- and all of App's elements.

Congrats! You've just set up Context in your React app, allowing all of your children components to have access to global state without prop drilling.

Context appears challenging, but once you get the hang of it, it makes prop usage and global state SO much easier. Happy coding!

Top comments (0)