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'.
Let's import some basic items we'll need:
import React, { createContext, useState, useEffect } from "react";
Then, at the top of our file, right below the import statements and before the function itself, we define the context.
const Context = createContext();
^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: "",
});
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 }) => {
2) Then, we initialize state with a user object, comprised of email and username in this example.
const [user, setUser] = useState({ email: "", username: "" });
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);
});
}, []);
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>
);
};
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);
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>
);
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)