useContext is a hook provided by React for accessing React’s Context in function components. Context is a React mechanism that allows you to share data throughout the component tree without having to pass props down manually at every level. It’s particularly suitable for handling global state such as themes, user information, language settings, etc.
Before useContext was introduced, accessing Context required using the Context.Consumer component, while useContext provides a more concise approach, especially in function components.
Basic Components of Context
Before using useContext, we need to understand the three core parts of Context:
createContext: Creates a Context object.Provider: The Context provider, used to wrap the component tree and provide shared data.ConsumeroruseContext: Used to consume data from the Context.
useContext is essentially a replacement for Consumer, simplifying the code.
How to Use useContext?
Here is the complete process for using useContext:
1. Create Context
Use createContext to create a Context object:
import { createContext } from 'react';
// Create Context, you can specify a default value (optional)
const MyContext = createContext('Default value');
2. Provide Context Data
Use the Provider component to wrap the component tree that needs to share data, and pass data through the value prop:
import React from 'react';
function App() {
const value = 'Hello from Context';
return (
<MyContext.Provider value={value}>
<Child />
</MyContext.Provider>
);
}
3. Consume Data with useContext
In a function component, access the Context value using useContext:
import { useContext } from 'react';
function Child() {
const contextValue = useContext(MyContext);
return <p>{contextValue}</p>; // Output: Hello from Context
}
Complete Example
import React, { createContext, useContext } from 'react';
const MyContext = createContext('Default value');
function App() {
const value = 'Hello from Context';
return (
<MyContext.Provider value={value}>
<Parent />
</MyContext.Provider>
);
}
function Parent() {
return <Child />;
}
function Child() {
const contextValue = useContext(MyContext);
return <p>{contextValue}</p>;
}
export default App;
Result: The page displays “Hello from Context”.
A More Practical Example: Managing Global State
useContext is commonly used for managing global state, such as theme switching. Here’s a complete theme switching example:
import React, { createContext, useContext, useState } from 'react';
// Create Context
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Toolbar />
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</ThemeContext.Provider>
);
}
function Toolbar() {
return <ThemedButton />;
}
function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);
const style = {
background: theme === 'light' ? '#fff' : '#333',
color: theme === 'light' ? '#000' : '#fff',
};
return (
<button style={style}>
Current Theme: {theme}
</button>
);
}
export default App;
- The
Appcomponent providesthemeandsetThemethrough theProvider. ThemedButtonusesuseContextto access and use these values.- Clicking the button toggles the theme, and the button style changes accordingly.
How useContext Works
- Find the Nearest Provider: When
useContext(MyContext)is called, React searches up the component tree for the nearestMyContext.Providerand returns itsvalue. - Default Value: If no matching
Provideris found in the component tree, it returns the default value specified whencreateContextwas called. - Dynamic Updates: When the
Provider’svaluechanges, all components usinguseContextwill re-render.
Note: If you use Context directly without a Provider, you’ll get the default value:
const MyContext = createContext('Default value');
function Child() {
const value = useContext(MyContext);
return <p>{value}</p>; // Output: Default value
}
function App() {
return <Child />;
}
Advantages of useContext
- Avoids Props Drilling: No need to pass data through props layer by layer, simplifying code.
- Concise and Easy to Use: Compared to
Consumer, the syntax ofuseContextis more intuitive. - Global State Management: Combined with
useStateoruseReducer, it can implement simple global state management.
Limitations of useContext
- Performance Issues:
- Every time a
Provider’svaluechanges, all components depending on that Context will re-render. - If
valueis an object, a new reference is generated with each render, potentially triggering unnecessary re-renders even if the content hasn’t changed. Solution: OptimizevalueusinguseMemo: ```jsx
const value = useMemo(() => ({ theme, setTheme }), [theme]);
```
- Every time a
- Not Suitable for Complex State Logic:
- When state logic becomes complex (e.g., requiring multiple reducers or middleware),
useContextmay fall short. In such cases, consider Redux or other state management libraries.
- When state logic becomes complex (e.g., requiring multiple reducers or middleware),
- Single Context Limitations:
- If a Context contains too much data, it becomes difficult to modularize. You can use multiple Contexts to separate concerns.
Using with useReducer
useContext is often paired with useReducer for more complex state management:
import React, { createContext, useContext, useReducer } from 'react';
const CountContext = createContext();
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function App() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<CountContext.Provider value={{ state, dispatch }}>
<Counter />
</CountContext.Provider>
);
}
function Counter() {
const { state, dispatch } = useContext(CountContext);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>Add 1</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Subtract 1</button>
</div>
);
}
export default App;
useReducermanages state logic, while `
Comments