React useContext
What is the useContext
hook in React?
The useContext
hook is a React hook that allows functional components to consume values from a React Context. Context provides a way to pass data through the component tree without manually passing props down at every level. useContext
simplifies state or data sharing between components by directly accessing the context value from any child component.
How does the useContext
hook work?
The useContext
hook takes a context object (created with React.createContext()
) as its argument and returns the current value of that context. The returned value is determined by the nearest matching Context.Provider
above the component in the tree. If no provider is found, it returns the default value defined in the context.
Example of using useContext
:
import React, { useContext } from 'react';
// Create a Context
const ThemeContext = React.createContext('light');
function DisplayTheme() {
const theme = useContext(ThemeContext); // Access the context value
return <p>Current theme: {theme}</p>;
}
function App() {
return (
<ThemeContext.Provider value="dark">
<DisplayTheme />
</ThemeContext.Provider>
);
}
In this example, the DisplayTheme
component accesses the current theme value using useContext
. The theme is provided by the ThemeContext.Provider
in the parent component.
What is a React Context?
React Context is a feature that allows you to share state or data between components without passing props manually at each level. Context consists of a Provider
component, which supplies the value, and a Consumer
component, which consumes the value. The useContext
hook simplifies the process by allowing functional components to directly consume the context value without needing a separate Consumer
component.
How do you create and use a context in React?
To create and use a context in React, follow these steps:
- Create a context using
React.createContext()
. - Use the
Context.Provider
component to provide a value to the context. - Use the
useContext
hook orContext.Consumer
to access the context value in child components.
Example of creating and using a context:
import React, { useContext } from 'react';
// 1. Create a Context
const UserContext = React.createContext();
function UserProfile() {
// 3. Consume the context value
const user = useContext(UserContext);
return <p>User: {user.name}</p>;
}
function App() {
const user = { name: 'John Doe', age: 30 };
return (
// 2. Provide the context value
<UserContext.Provider value={user}>
<UserProfile />
</UserContext.Provider>
);
}
In this example, UserContext
is created to share the user data. The App
component provides the user object, and UserProfile
consumes the user object using useContext
.
What are the benefits of using useContext
?
The useContext
hook provides several benefits:
- Reduces prop drilling:
useContext
allows you to pass data down the component tree without manually passing props at every level, eliminating the need for prop drilling. - Simplifies state sharing:
useContext
enables easier sharing of global state, such as theme, user authentication, or language settings, between components. - Cleaner code: Functional components can directly access context values using
useContext
, making the code easier to read and maintain without usingConsumer
components.
What is prop drilling, and how does useContext
help avoid it?
Prop drilling occurs when you pass props through multiple levels of the component tree to reach a deeply nested component that needs the data. This can make the code more complex and harder to maintain, especially when several components do not directly need the props but are used solely to pass them down.
useContext
helps avoid prop drilling by allowing components to access context values directly from any level in the component tree, without passing props down manually.
Example of prop drilling:
function Parent({ user }) {
return <Child user={user} />; // Passing props to Child
}
function Child({ user }) {
return <Grandchild user={user} />; // Passing props to Grandchild
}
function Grandchild({ user }) {
return <p>User: {user.name}</p>;
}
function App() {
const user = { name: 'John Doe' };
return <Parent user={user} />; // Prop drilling
}
Example of avoiding prop drilling with useContext
:
const UserContext = React.createContext();
function Parent() {
return <Child />;
}
function Child() {
return <Grandchild />;
}
function Grandchild() {
const user = useContext(UserContext); // Accessing context directly
return <p>User: {user.name}</p>;
}
function App() {
const user = { name: 'John Doe' };
return (
<UserContext.Provider value={user}>
<Parent />
</UserContext.Provider>
);
}
In the second example, useContext
eliminates the need for passing props through every component level, simplifying the code.
What is the Context.Provider
component?
The Context.Provider
component is used to supply a context value to all components that consume the context. It wraps around child components, allowing them to access the provided context value. The value
prop of Provider
determines the data that will be shared with the consuming components.
Example of using Context.Provider
:
const ThemeContext = React.createContext();
function App() {
return (
<ThemeContext.Provider value="dark">
<DisplayTheme />
</ThemeContext.Provider>
);
}
In this example, the theme value "dark" is passed down to any component inside the ThemeContext.Provider
that uses useContext
.
What happens if you consume a context without a Provider
?
If you consume a context using useContext
without wrapping the component in a corresponding Context.Provider
, React will return the default value of the context (if one is defined when calling React.createContext
). If no default value is provided, useContext
will return undefined
.
Example with a default value:
const ThemeContext = React.createContext('light');
function DisplayTheme() {
const theme = useContext(ThemeContext);
return <p>Current theme: {theme}</p>;
}
function App() {
return <DisplayTheme />; // No Provider, so it uses the default value
}
In this example, DisplayTheme
uses the default value "light" because there is no ThemeContext.Provider
wrapping it.
Can you update context values dynamically?
Yes, you can update context values dynamically by wrapping the Provider
component with state or functions that update the context value. By passing a state variable as the context value, child components consuming the context will automatically receive the updated value when it changes.
Example of dynamically updating context values:
import React, { useState, useContext } from 'react';
const ThemeContext = React.createContext();
function ThemeToggle() {
const { theme, toggleTheme } = useContext(ThemeContext);
return <button onClick={toggleTheme}>Toggle to {theme === 'light' ? 'dark' : 'light'}</button>;
}
function App() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<div>Current theme: {theme}</div>
<ThemeToggle />
</ThemeContext.Provider>
);
}
In this example, the theme value is stored in the component's state and updated via the toggleTheme
function. The updated theme is provided to the context and accessed by child components.
What are the limitations of useContext
?
While useContext
is useful for sharing data across components, it has some limitations:
- Re-rendering: When the context value changes, all consuming components re-render, even if the value they consume has not changed. This can impact performance in large applications.
- Single source: Context is designed for global state sharing, and using it for more localized state can lead to over-complication.
- Read-only consumption: By default,
useContext
provides read-only access to the context value. If you want to allow dynamic updates, you need to manage state or functions in the provider.