When you build applications with React, sometimes you need to pass information such as theme settings or user data to many components within your app. React Context is a feature that simplifies this process, helping you avoid the complexity of passing props through multiple layers of your component hierarchy. For instance, instead of passing a user's preferences from one component to another, Context allows any component to directly access these preferences. This topic provides a foundational understanding of React Context, demonstrating how to set it up and use it to keep your code neat and organized; this makes it easier to maintain and navigate as your app grows.
Understanding react context
Passing props is a useful method to directly send data through your user interface tree to the components that use it. However, passing props can become lengthy and inconvenient when you need to send a prop deep into the tree, or if multiple components require the same prop. The nearest common ancestor might be quite distant from the components that need data, and lifting state up to that level can result in a situation known as prop drilling.
The React context API lets you create global variables that can be distributed throughout a React app. This offers an alternative to prop drilling, or moving props from grandparent to child to parent, and so on.
React Context lets you share values, such as the current theme (light or dark), or the data of the logged-in user, across your entire app. This makes these values available to any component that requires them.
React.createContext() is all you need. It gives you a consumer and a provider. The Provider is a component that, as the name implies, provides the state to its children. It holds the "store" and acts as a parent to all components that may need that store. The Consumer, on the other hand, is a component that consumes and utilizes the state.
Creating a context
Let's begin by creating a new context. You can do this by using the React.createContext() function, which you can assign to a variable:
import React, { createContext } from 'react';
const ThemeContext = React.createContext();
The createContext() function can accept an argument for the default context value, which is used when a component does not have a matching Provider above it in the component tree. This object contains a Provider component, which you'll use to wrap your component tree, and a Consumer component or the useContext hook, which allows you to access the Context data.
Using provider to pass context
To utilize the context you've established, you need to enclose your components with the Context Provider. The Provider component allows a value prop to be shared with consuming components that descend from this Provider. The value prop assigned to the Provider component becomes the context value, which is available to consuming components. To initialize context, generate a ThemProvider.jsx component and include:
import React, { createContext, useState } from "react";
// Create a Context for the theme
const ThemeContext = createContext();
function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
};
const value = { theme, toggleTheme };
// Return the Provider component with its value
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
export { ThemeContext, ThemeProvider };Using context in components
To use the Context in your components, you can use either the Consumer component or the useContext hook. The useContext hook is generally preferred for its simplicity and readability. Let's create a ThemedButton.jsx component to use the context we've set up.
import React, { useContext } from "react";
import { ThemeContext } from "./ThemeProvider";
function ThemedButton() {
// Access the theme and toggleTheme function from ThemeContext
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div className="flex items-center justify-center w-full h-[100vh]">
<button
onClick={toggleTheme}
className="p-6 rounded-md text-lg"
style={{ backgroundColor: theme === "light" ? "#FFF" : "#333" }}
>
Toggle Theme
</button>
</div>
);
}
export default ThemedButton;
In this example, ThemedButton has access to the theme and toggleTheme without the need for props. When clicked, the button toggles the theme between light and dark.
import React from 'react';
import { ThemeProvider } from './ThemeProvider';
import ThemedButton from './ThemedButton';
const App = () => {
return (
<ThemeProvider>
<div>
<ThemedButton />
</div>
</ThemeProvider>
);
};
export default App;
The ThemeProvider component wraps the part of the app that needs access to the theme context. This allows any child components to access the theme values and functions provided by ThemeProvider. ThemedButton is a child component that uses the theme context. It can access the theme state and toggleTheme function to change the theme of the app.
Using default value in context
At times, you may wish to set a default value for your context. This value is used when a component lacks a corresponding Provider in the component tree. This can be particularly useful for testing components in isolation without wrapping them with a Provider, or for the case where you have optional context providers.
Here's how you can define a default value when creating a context:
import React, { createContext } from 'react';
const defaultThemeValue = 'light';
const ThemeContext = React.createContext(defaultThemeValue);
In the above example, you set the defaultThemeValue to 'light'. This indicates that if a component tries to use the ThemeContext and the ThemeProvider is not found in the component tree, it will default to the 'light' theme.
Conclusion
React Context is a feature that makes state management in your application more straightforward. You learned how to create a Context, provide values using a Provider, and use those values in your components with the useContext hook through the Theme Context example. This method can significantly enhance the maintainability and scalability of your React applications, particularly as they become more complex.