React is a powerful JavaScript library for building user interfaces, and one of its core principles is the unidirectional data flow. However, as applications grow, managing state and passing data through the component tree can become cumbersome. This is where the Context API comes into play.
The Context API in React provides a way to share values between components without explicitly passing props through every level of the component tree. It is particularly useful for global state management, such as theme settings, authenticated user details, or application settings.
The Context API allows you to create a context object, which holds the global data. Components that need to access this data can subscribe to the context and retrieve the values directly.
Let’s dive into the steps to create and use context in a React application.
To create a context, use the createContext
method from React
import React, { createContext } from 'react';
const MyContext = createContext();
This code creates a context object called MyContext
.
The context provider is a component that uses the Provider
component from the created context to supply the context value to its children.
import React, { createContext, useState } from 'react';
// Create a Context
const MyContext = createContext();
const MyProvider = ({ children }) => {
const [value, setValue] = useState("Hello, World!");
return (
{children}
);
};
export default MyProvider;
Here, MyProvider
is a component that wraps its children with the MyContext.Provider
. The value
prop of the provider holds the context data.
To consume the context in a component, use the useContext
hook.
import React, { useContext } from 'react';
import MyProvider, { MyContext } from './MyProvider';
const MyComponent = () => {
const { value, setValue } = useContext(MyContext);
return (
{value}
);
};
const App = () => (
);
export default App;
In MyComponent
, we use the useContext
hook to access the value
and setValue
from MyContext
. The App
component wraps MyComponent
with MyProvider
to provide the context.
When you run the above code, the initial output will be:
Hello, World!
When you click the button, it will change to:
New Value!
Let’s explore some advanced concepts and patterns for using the Context API.
You can use multiple contexts by nesting providers.
const FirstContext = createContext();
const SecondContext = createContext();
const FirstProvider = ({ children }) => {
const [firstValue, setFirstValue] = useState("First");
return (
{children}
);
};
const SecondProvider = ({ children }) => {
const [secondValue, setSecondValue] = useState("Second");
return (
{children}
);
};
const NestedProviders = ({ children }) => (
{children}
);
const MyComponent = () => {
const { firstValue, setFirstValue } = useContext(FirstContext);
const { secondValue, setSecondValue } = useContext(SecondContext);
return (
{firstValue}
{secondValue}
);
};
const App = () => (
);
export default App;
Initially, the output will be:
First
Second
After clicking the buttons, it will change to:
Updated First
Updated Second
You can pass dynamic values to the context provider.
const DynamicProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
return (
{children}
);
};
const ThemeComponent = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
Current Theme: {theme}
);
};
const App = () => (
);
export default App;
Initially, the output will be a light-themed UI:
Current Theme: light
After clicking the “Toggle Theme” button, it will switch to a dark-themed UI:
Current Theme: dark
useMemo
.
const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
// Usage in a component
const MyComponent = () => {
const { theme, toggleTheme } = useTheme();
return (
Current Theme: {theme}
);
};
In this example, we’ll build a simple application that allows users to switch between light and dark themes.
Step-by-Step Implementation
First, create a new context for the theme.
// ThemeContext.js
import React, { createContext, useState, useContext } from 'react';
const ThemeContext = createContext();
export const useTheme = () => {
return useContext(ThemeContext);
};
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
{children}
);
};
Create a component that consumes the theme context.
// ThemeComponent.js
import React from 'react';
import { useTheme } from './ThemeContext';
const ThemeComponent = () => {
const { theme, toggleTheme } = useTheme();
const styles = {
backgroundColor: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#000' : '#fff',
padding: '20px',
textAlign: 'center',
};
return (
Current Theme: {theme}
);
};
export default ThemeComponent;
Finally, set up the App
component to use the ThemeProvider
// App.js
import React from 'react';
import { ThemeProvider } from './ThemeContext';
import ThemeComponent from './ThemeComponent';
const App = () => {
return (
);
};
export default App;
When you run this application, you’ll see a page with a white background and black text (light theme). Clicking the “Toggle Theme” button will switch to a dark theme with a black background and white text.
In this example, we’ll build a basic authentication system where users can log in and log out. The authentication status will be shared across the application using the Context API.
Step-by-Step Implementation
First, create a new context for authentication.
// AuthContext.js
import React, { createContext, useState, useContext } from 'react';
const AuthContext = createContext();
export const useAuth = () => {
return useContext(AuthContext);
};
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const login = (username) => {
setUser({ name: username });
};
const logout = () => {
setUser(null);
};
return (
{children}
);
};
Create a component that consumes the authentication context.
// AuthComponent.js
import React from 'react';
import { useAuth } from './AuthContext';
const AuthComponent = () => {
const { user, login, logout } = useAuth();
return (
{user ? (
<>
Welcome, {user.name}!
>
) : (
<>
Please log in.
>
)}
);
};
export default AuthComponent;
Finally, set up the App
component to use the AuthProvider
.
// App.js
import React from 'react';
import { AuthProvider } from './AuthContext';
import AuthComponent from './AuthComponent';
const App = () => {
return (
);
};
export default App;
When you run this application, you’ll initially see a message asking you to log in. After clicking the “Login” button, the message will change to welcome the user, and a “Logout” button will appear. Clicking the “Logout” button will revert the state back to the initial login prompt.
The Context API is a valuable tool for managing global state in React applications. By understanding how to create, provide, and consume context, you can simplify state management and reduce the complexity of prop drilling. While it's a powerful feature, it's important to use it judiciously and follow best practices to maintain performance and code maintainability. Happy coding !❤️