In React, Context provides a way to pass data through the component tree without having to pass props manually at every level. It allows you to share values across the entire application effortlessly. It enables components to subscribe to a context and access its value anywhere in the component tree, regardless of how deep the component is nested.
When to Use Context?
Context is useful in scenarios where certain data needs to be accessed by many components at different levels of the component tree.
Problems that can occur when data is delivered only by Props
function App() { return <GrandParent value="Hello World!" />; } function GrandParent({ value }) { return <Parent value={value} />; } function Parent({ value }) { return <Child value={value} />; } function Child({ value }) { return <GrandChild value={value} />; } function GrandChild({ value }) { return <Message value={value} />; } function Message({ value }) { return <div>Received: {value}</div>; }
These codes are called Props Drilling. It's okay to pass Props through one or two components, but it's going to be inconvenient to pass through four like this. For example, if you're trying to open a
Message
component and figure out where thisvalue
is coming from, it's very inefficient because you have to keep riding back to that parent component.Problems solved with using Context
import { createContext, useContext } from 'react'; const MyContext = createContext(); function App() { return ( <MyContext.Provider value="Hello World"> <GrandParent /> </MyContext.Provider> ); } function GrandParent() { return <Parent />; } function Parent() { return <Child />; } function Child() { return <GrandChild />; } function GrandChild() { return <Message />; } function Message() { const value = useContext(MyContext); return <div>Received: {value}</div>; }
This way, you can erase the props that you were delivering through multiple components in the middle.
How to use Context?
Call
createContext
outside of any components to create a context.The Context object contains a component called
Provider
, and if you set thevalue
you want to share between the components, the child components can access it immediately.Call
useContext
at the top level of your component to read and subscribe to context.
Example 1 (
value
is string)import { createContext, useContext } from 'react'; // 1. Creat context const ThemeContext = createContext(null); export default function MyApp() { return ( // 2. Provide context <ThemeContext.Provider value="dark"> <Form /> </ThemeContext.Provider> ) } function Form() { return ( <Panel title="Welcome"> <Button>Sign up</Button> <Button>Log in</Button> </Panel> ); } function Panel({ title, children }) { // 3. Consume context const theme = useContext(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ children }) { // 3. Consume context const theme = useContext(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); }
Example 2 (
value
is object and changeable)// ThemeContext.js import { createContext, useState } from 'react'; // 1. Creat context const ThemeContext = createContext(); export const ThemeProvider = ({ children }) => { const [theme, setTheme] = useState('light'); const toggleTheme = () => { setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light')); }; return ( // 2. Provide context <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); }; export default ThemeContext;
// App.js import { ThemeProvider } from './ThemeContext'; import Header from './Header'; import Button from './Button'; function App() { return ( <ThemeProvider> <Header /> <Button /> </ThemeProvider> ); } export default App;
// Header.js import { useContext } from 'react'; import ThemeContext from './ThemeContext'; function Header() { // 3. Consume context const { theme } = useContext(ThemeContext); return <header style={{ backgroundColor: theme }}>Header</header>; } export default Header;
// Button.js import { useContext } from 'react'; import ThemeContext from './ThemeContext'; function Button() { // 3. Consume context const { toggleTheme } = useContext(ThemeContext); return <button onClick={toggleTheme}>Toggle Theme</button>; } export default Button;