When I started to learn React, I was overwhelmed by the concept of Context so I immediately forgot everything I read and decided I would just never use it. Then I had to drill state and setState 5+ components deep in a bootcamp project and I thought maybe I should give Context another chance. At that point I was more familiar with state and component trees and armed with that confidence (and a playlist of youTube videos) I set out to understand and implement Context in my app, and lets just say I nailed it. Here is my simplified explanation for anyone just starting out with Context.
What is Context?
Context is a way of accessing data through your whole component tree without having to pass props down manually at every level. Once Context is created you can import it in any component you wish to access the data. Simply put, its like creating a global state.
When to use Context?
I had read a few suggestions to use Context sparingly which was part of the reason I didn't spend time learning it initially, and frankly, I don't heed that advice now. For me, Context makes my code feel a lot cleaner and better managed. If I need access to a piece of state in 3 or more components, I'm using Context. That is especially true if I'm going update the state and I want the change to persist across all pages of my app. Common examples people use for Context are setting a dark theme and referencing if a user logged in. I personally use it often with data I have fetched from an API.
How to do it:
We will need to do two things to use Context:
Establish Context with
createContext
and wrapping components that will use the context in aContext.Provider
.Importing the context in the consumer component with
useContext
. (With a bonus method of importing Context at the end!)
First, decide where you want to store your context data. I like to put it in a separate Context component and import it to my App component. This keeps my code tidy but you can also establish Context directly in your App component. Like state, you just want to make sure it lives in a parent to all the components that will eventually import the data. If you are storing context in a Context component like me, you will need to pass it a {children}
prop.
In the Context component import createContext
from react with your other hooks. In this example, I'm going to useState
to store the data we will pass as context to the consumer components.
Next we will need to use the createContext
hook to declare our context outside our Context component using the createContext()
function. We declare the context outside the Context component because we are going to export it for use in our consumer components.
Your code should look like this so far:
Now we need to tell React what data our DataContext
we just created will hold. This is usually where I would useEffects
to fetch data from an API and assign it to a state variable but to keep our example clean and simple I'm just going to declare the state with a default value of an array of numbers.
From here we create the 'provider component' for the DataContext
as a return from our Context component. This DataContext.Provider
component takes one prop called value
which we assign data
(our state from the previous step) and wraps around the {children}
prop we gave to our Context component earlier. Like this:
What if we wanted to use setData
in another component of the app? Well, we could declare another context and call it something like UpdateDataContext
and wrap both our child and DataContext.Provider
in the UpdateDataContext.Provider
. If this were unrelated data, I would probably go ahead and do that.
Instead since setData
is part of our data state, in our DataContext.Provider
we set our value to an array of [data, setData]
. This will pass both our data
and setData
function to our children components in our single DataContext.Provider
.
The final step in our provider component process is to import our Context component to our App component and wrap it around all the children, or 'consumer components' we want to pass our data to.
At this stage, our data
and setData
are available to all the components in our app without having to pass any props! Amazing, but how do we access them? Thats step 2...
useContext
Lets go directly to ComponentC, bypassing components A and B. Here we will import useContext
from react as well as our DataContext
from our Context component.
We'll then take the useContext
hook, pass it DataContext
and declare the variables we will be setting the data to in ComponentC.
Viola! You now have access to your data
state and setData
function in ComponenetC to use as you wish with out having to pass the prop through multiple components.
TLDR:
To recap, in order to create globally accessible state with Context in your app we:
Created the Context Provider by:
- Creating a Context component and passing it a prop of
{children}
. - Importing
createContext
from react - Declaring and exporting our
DataContext
fromcreateContext()
outside the Context component. - Declaring the state we will use as our context data inside the Context component.
- Wrapping our
{children}
in theDataContext.Provider
that has one prop of value and setting it to the state we declared in step 4. - Exporting our Context component and importing it in the App component to wrap around all the components in our app.
Accessed the Context in our consumer component by:
- Importing
useContext
from react andDataContext
from our Context component in our consumer component. - Assigning our
data
andsetData
variables usinguseContext
and passing it ourDataContext
.
Bonus!
The above works just fine but one more thing I like to do to streamline my code is rather than importing useContext
in my consumer, I export a function that returns useContext()
from my Context component so I only have to import this function to to access my data in my consumer components. This is what it looks like in both Context
and ComponenetC
.
What we did was:
- Import
useContext
in theContext
component and remove it fromComponentC
. - Export the
useData
function that returnsuseContext(DataContext)
. - Imported
useData
in ComponentC.
Thats it! After setting up Context once or twice I felt a lot more confident but I did reference my notes on future tries so write your own workflow or save this blog for future reference!
Top comments (4)
I have one question. is this re-render the componentA and B while updating the value.
Hi! Any component that uses the "data" state that we assigned to our context will re render if you assign a new array with the setData function.
i am not sure. i have some doubt. if i update(setState) the value in componentC does it reRender the component A and B?
If the state is updated inside the context component, it will cause a re-render on all the components that use the context and consume that state via the useContext hook. This is because, whenever the state inside the context changes, React will trigger a re-render on all the components that are subscribed to that context. However, components that do not use the context will not be affected by the state change and will not be re-rendered.