The React Context provides a way to pass data through the component tree without having to pass props
down manually to every level. In React, data is often passed from a parent to its child component as a property.
Context is like a global object to the React component sub-tree 🌐.
Let's look at the below diagram 📈.
Component A
is clearly the main parent component with immediate children components B, C. These components can receive params from component A
and pass that data to the children components, but what about a scenario where Component E
needs data from Component A
and that data is not needed in Component B
then passing that data to Component B
becomes redundant.
Props drilling, a term to describe when you pass props down multiple levels to a nested component, through components that don't need it.
This is the benefit of React context - it provides a cool way 😎 of making data readily available to every single child component in the React Application.
As far we get to know that React context allows us to pass down and use (consume) data in whatever component we need in our React app without using props
.
Using the new React Context API depends on four
main steps:
🔸 Create Context using the createContext
method. This function then returns an object with a Provider and a Consumer.
import React from 'react';
const AuthContext = React.createContext();
🔸 Next, use the Provider component to wrap around the parent/main component.
🔸 Wrap child components in the Provider component and make it accept a prop called value
. This value
can be anything!
<AuthContext.Provider value={value}>
<Demo />
</AuthContext.Provider>
🔸 Use the Consumer component anywhere below the Provider in the component tree to get a subset of the state.
function Demo() {
return (
<AuthContext.Consumer>
{value => <h1>{value}</h1>}
</AuthContext.Consumer>
);
}
📌 Let's see the full example:
import React from 'react';
export const AuthContext = React.createContext();
export default function App() {
return (
<AuthContext.Provider value="Happy">
<Demo />
</AuthContext.Provider>
)
}
function Demo() {
return (
<AuthContext.Consumer>
{value => <h1>{value}</h1>} /* prints happy */
</AuthContext.Consumer>
);
}
Above our App component, we are creating context with React.createContext()
and putting the result in a variable, AuthContext
.
Note that we can pass an initial value to our value prop when we call React.createContext().
Provider
and Consumer
, both of which are components. Things to keep note down what Provider actually do ✍️:
➡️ It holds one single javascript value that can be anything: an array, a function or an object.
➡️ It won’t cause its children to re-render: The context’s provider’s direct children won’t re-render every time the provider renders, but the consumers will. This rule is only valid for re-renders caused by the provider internally (state), but if his parent re-renders (props), the provider’s children will re-render as well.
➡️ All subscribers will re-render when the context value changes.
In our App component, we are using AuthContext. Specifically AuthContext.Provider
, To pass our value
down to every component in our App, we wrap our Provider component around it and in this case, Demo
.
On AuthContext.Provider
, we put the value that we want to pass down our entire component tree. We set that equal to the value
prop to do so. (here, Happy).
In Demo
, or wherever we want to consume what was provided in our context, we use the consumer component: AuthContext.Consumer
To use our passed-down value, we use what is called the render props pattern.
It is just a function that the consumer component gives us as a prop
. And in return for that function, we can return and use that value
.
📌 Here is the same example using useContext:
import React from 'react';
export const AuthContext = React.createContext();
export default function App() {
return (
<AuthContext.Provider value="Happy">
<Demo />
</AuthContext.Provider>
)
}
function Demo() {
const value = React.useContext(AuthContext);
return <h1>{value}</h1>;
}
useContext accepts the context type as parameter and returns the context value of the nearest provider of that type. If there is no such provider, then the default context value will be returned.
🔹 In short, you app’s performance will decrease drastically if your provider does a lot of work, for example having a value that combines a lot of separate values, you will have a lot of consumers of the same provider, and they will all re-render.
🔹 When the provider’s wrapper re-renders due to an internal cause (may be state), its children won’t re-render, only a consumer will. It is like your provider’s value teleports from the provider to the consumers directly ignoring everything in between.
🔹 So, it is more than okay to have multiple contexts and providers.
As you can see, the concepts involved are actually not that different from Redux.
So does context replaces redux?
The answer is NO🙅.
Redux isn’t only a way to pass down props( teleports them), it allows persistence, supports middlewares, and has a lot more advantages. My recommendation is to use Redux for complex global state management and Context for prop drilling.
As this article isn’t meant to talk about redux, so, I will drop some useful resources to read more about this comparison 👇.
In this article, we explored how we can easily use React Context instead of passing down props to share data between components 🚢. Depending on your use case, you might prefer to use simple props, React Context or even a third-party library like Redux to share data between your components.
🔊 Let's talk about `React Context` 🔊
— Adyasha M⚡ (@Adyasha8105) January 18, 2022
🔸React context allows us to pass down and use data in whatever component we need in our React app without using `props`.
🔸In other words, It is a way to manage the state globally 🌐.
Isn't it interesting😯?
Keep coding 😉. Thank you for reading 💖.
Feel free to connect on Twitter :)