Imagine you have a piece of data, like a user's name, that you need to use in several components of your app. You will need to pass this data down through every component of your application as a prop even if some components don’t actually need the data. This is called prop drilling and it can make our code messy and hard to maintain. To avoid prop drilling, React uses the context API, which allows us to establish a 'context' that stores the data and can be retrieved by any component in the tree.
What is Context API?
Context API is a feature in React that allows us to share data or state across multiple components in our application without having to manually pass props through every level of the component tree. The context API is useful when we need to access data across different components of our application.
For example, same theme settings are used in all the components of our application, user details are also shared across the different components, language preferences are also same across different components of the application. We use Context API to perform all these actions.
To use Context API in React, we need to create a 'context' which is shared all over the application. To create a context, we can use the function createContext() which returns a context object that has two components : a provider and a consumer. The provider component is responsible for making context data available to its child components. It wraps around the components that require access to the context. The consumer component accesses the context data provided by the Provider. It enables any component in the context tree to consume data without having to pass it through props.
Implementing Context API
Let’s explore how we can set up and use the Context API in our React application. We use the following steps to set up Context in our application.
First, we need to create a context that will hold our user data. This is done using the createContext() function from React.
// UserContext.js
import React from "react";
const UserContext = React.createContext();
export default UserContext;In this file, we create a UserContext using React.createContext(). This context will be used to provide and consume user data throughout our application.
Next, we set up a UserContextProvider component. This component will use the UserContext.Provider to wrap around other components, providing them with access to the context data.
// UserContextProvider.js
import React, { useState } from "react";
import UserContext from "./UserContext";
const UserContextProvider = ({ children }) => {
const [user, setUser] = useState(null);
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
export default UserContextProvider;The UserContextProvider component maintains the user state using the useState hook and provides both user and setUser through the context. Any child component wrapped by UserContextProvider will have access to this context.
After creating the context, we need to 'consume' it. It means we need to use it in our application. We do that by following these steps:
In our main application component, we wrap the components that need access to the context with the UserContextProvider.
// App.js
import "./App.css";
import Login from "./components/Login";
import Profile from "./components/Profile";
import UserContextProvider from "./context/UserContextProvider";
function App() {
return (
<div>
<UserContextProvider>
<Login />
<Profile />
</UserContextProvider>
</div>
);
}
export default App;
The Login component is used to enter user's credentials and submit them. It uses the useContext hook to access the setUser function from UserContext and update the user data.
// Login.js
import React, { useContext, useState } from "react";
import UserContext from "../context/UserContext";
function Login() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const { setUser } = useContext(UserContext);
const handleSubmit = (e) => {
e.preventDefault();
setUser({ username, password });
};
return (
<div>
<h3>Login Page</h3>
<input
type="text"
placeholder="Enter username"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Enter password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleSubmit}>Submit</button>
</div>
);
}
export default Login;The Login component uses useContext to access setUser and updates the context with the entered username and password when the form is submitted.
The Profile component consumes the user context to display the username. It also handles the case where no user is logged in.
// Profile.js
import React, { useContext } from "react";
import UserContext from "../context/UserContext";
function Profile() {
const { user } = useContext(UserContext);
if (!user) return <h1>Not logged in!</h1>;
return (
<div>
<h1>Your username: {user.username}</h1>
</div>
);
}
export default Profile;The Profile component checks if a user is logged in by accessing the user from the context. If a user is present, it displays the username; otherwise, it shows a "Not logged in!" message. We see a "Not logged in!" message the first time we render the application.
Conclusion
By using the Context API, we've effectively shared username across multiple components without prop drilling. This approach simplifies state management and keeps your code clean and maintainable. Whether you're handling user authentication, theme settings, or any global state, the Context API provides a clean and efficient solution. I hope this guide has helped you understand how to implement and use the Context API in your React projects.