Exploring React Hooks: useContext
Exploring the Power of useContext Hook in React
Welcome back to our Exploring React Hooks series. In our previous article, we explored how to use useEffect effectively. In this installment, we'll be focusing on the useContext
hook, which enables us to easily access context values in our components. So let's get started!
Understanding Context:
Before we dive into the useContext
hook, let's quickly recap what React context is.
Context allows us to share data between components that are not directly connected in the component hierarchy.
Context provides a way to pass data through the component tree without having to pass props manually at every level.
It can be very useful in scenarios where multiple components need access to the same data, such as theme preferences, localization settings, or user authentication.
What is the useContext Hook?
The useContext hook is a built-in hook provided by React that allows components to consume values from a Context.
With the useContext
hook, you can access the current value of a Context directly within a functional component, eliminating the need to use a Context.Consumer
component or prop drilling.
Using the useContext Hook
To utilize the useContext
hook:
Import and create a Context using the
createContext()
function.This Context can then be provided to other components using a
Context.Provider
component.Within the consuming components, we can import and call
useContext(Context)
to access the current value provided by theContext.Provider
.
The Props Way
Let's say we have a scenario where there are multiple nested components and both the top and bottom components need access to the state. We can pass the state as props through each nested component. This process is commonly referred to as prop drilling.
We will be creating five components, where each component will render a heading element with a different character from the Game of Thrones series, forming a nested hierarchy of components.
Our main purpose is to access the user value inside ComponentE
that will be provided by ComponentA
.
ComponentA:
import React, { useState } from "react";
import ComponentB from "./ComponentB";
function ComponentA() {
const [user, setUser] = useState("Jon Snow");
return (
<>
<h2>{`${user} of House Stark!`}</h2>
<ComponentB user={user} />
</>
);
}
export default ComponentA;
ComponentB:
function ComponentB({ user }) {
return (
<>
<h3>Daenerys Stormborn of House Targaryen!</h3>
<ComponentC user={user} />
</>
)};
ComponentC:
function ComponentC({ user }) {
return (
<>
<h3>Cersei Lannister of House Lannister!</h3>
<ComponentD user={user} />
</>
)};
ComponentD:
function ComponentD({ user }) {
return (
<>
<h3>Robert Baratheon of House Baratheon!</h3>
<ComponentE user={user} />
</>
)};
ComponentE:
import React, { useContext } from "react";
import { UserContext } from "./ComponentA";
function ComponentE({ user }) {
return (
<>
<h2>{`Hello ${user}, King in the North!!`}</h2>
</>
);
}
export default ComponentE;
Output:
Import the ComponentA
into your App
Component and run the server. This should be the expected result:
In the above example, we had to use prop drilling to pass the user
value from the top-level component (ComponentA
) down to the nested components (ComponentB
, ComponentC
, ComponentD
), that did not need the data themselves, only to pass it down to a deeper component(ComponentE
) that required it.
The Context Way
With useContext
, we can avoid this unnecessary passing of props through intermediate components. We can directly access the required data from the context without explicitly passing it through component props.
We'll be using the previous example. Let's check it out:
ComponentA
This component will serve as the top-level component and the provider of the UserContext
.
To create context, we must import createContext
and initialize it. We will then create and export the UserContext
using the createContext()
function.
We'll provide the UserContext
with the value of user
using the UserContext.Provider
component. This will allow any nested components that consume this context to access the value.
import React, { createContext, useState } from "react";
import ComponentB from "./ComponentB";
export const UserContext = createContext();
function ComponentA() {
const [user, setUser] = useState("Jon Snow");
return (
<>
<UserContext.Provider value={user}>
<h2>{`I am ${user} of House Stark!`}</h2>
<ComponentB/>
</UserContext.Provider>
</>
);
}
export default ComponentA;
Now, all components in this tree will have access to the UserContext
.
ComponentE
In order to use the Context in a child component, we need to access it using the useContext
Hook.
First, we include the useContext
in the import statement. Then we can access the UserContext
in this component.
import React, { useContext } from "react";
import { UserContext } from "./ComponentA";
function ComponentE() {
const user = useContext(UserContext);
return (
<>
<h2>{`Hello ${user}, King in the North!!`}</h2>
</>
);
}
export default ComponentE;
The UserContext
is created in ComponentA
and is passed down through the nested components (ComponentB
, ComponentC
, ComponentD
, and ComponentE
). The user
value is provided by ComponentA
and accessed by ComponentE
using the useContext
hook.
Output:
Import ComponentA
inside App.jsx
and run the server:
As evident, the resulting output remains unchanged in both cases.
However, by utilizing the useContext
hook, we have effectively replaced the need for passing props down the component tree. This approach simplifies our code, enhances reusability, and makes it easier to manage shared data across the application.
Context Considerations
Here are some considerations to keep in mind when using Context:
Don't use Context to avoid drilling props down just one or two layers:
Context is great for managing state which is needed by large portions of an application. However, If you only need to pass data down a few layers, prop drilling can be a faster and simpler solution.
Avoid using Context to save state that should be kept locally:
If the state is specific to a particular component or doesn't need to be accessed by other components, it's best to use local state rather than Context.
Wrap the Provider component around the lowest common parent:
You should always wrap the Provider around the lowest possible common parent in the tree where the shared state is needed.
To Summarize
The useContext
hook can be used anywhere inside a functional component, enabling easy access to the context value without any prop drilling. Once we have the context value, we can utilize it within our component for rendering, state management, or any other purpose we require.
The useContext
hook simplifies our code and allows us to create applications that are easier to scale and maintain.
References
Thank's for taking the time to read this blog. Hope you found it informative and enjoyable! Connect with me on Twitter.
Stay tuned for our next article: useRef Hook.
Catch you guys on the next one. Cheers .. ✌️