As always, this is a post I'm using to help teach myself something! Hopefully this helps you too.
This post does assume a basic knowledge of React, with a degree of familiarity with the concept of state.
State -- A Reintroduction
I would not be surprised if you've heard of useState
, which is a hook provided by React that allows us to set information dynamically, and monitor it in a manner that will allow our components to change along with updates to that information.
import React, { useState } from 'react';
function App () {
const [ playList, setPlayList ] = useState ([
{
id:1,
genre:"Alternative",
title:"You and I",
artist:"Toro y Moi",
track: "/tracks/you_and_i.mp3"
}, {
id:2,
genre:"Rock",
title:"Khuda Bhi Aasman",
artist:"Khruangbin",
track: "/tracks/khuda_bhi_aasman.mp3"
}, {
id:3,
genre:"Rock",
title:"Goodie Bag",
artist:"Still Woozy",
track: "/tracks/goodie_bag.mp3"
}
])
return(
<div className=music-box>
<MusicBox playlist={playlist} setPlaylist={setPlaylist} />
</div>
)}
export default App
Take this application above... Well let's just say this one piece, of this one, small application...
The information that we are putting into state is our playlist, and we're passing that down via props. Cool. We can imagine that MusicBox
will probably use the information to populate some interface.
But what if our application isn't so small?
What if nested inside of Music box are tens of elements, in charge of various functionalities ranging that could include:
- A playlist interface that can be re-ordered, added to, deleted from, etc.
- An album image component that uses a pre-associated relationship to the track playing.
- A audio visualization component that displays an equalizer that responds to currently playing music.
- A suggested tracks component that takes the genre and returns tracks of the same kind.
Maybe all of the above???
If we wanted to bake all of that functionality into our app, chances are we're going to have a ton of components, in various branches of the application, that would all need access to the current playlist
value. Also, it would be a safe bet that not every component would need every value inside of playlist
. We could still pass everything down manually via prop drilling
but that could get cumbersome if we are also passing other prop values declared within smaller subsections of the application.
Enter Context
This precise kind of dilemma is the exact reason context
exists. Imagine if you only had to declare your state that a whole application might need pieces of in a single spot, and only call those values when and where you needed them, no need for prop drilling!
So now we need to do that instead for our application.
Step 1: Create Context and the Provider
Think of it like this, Context
is the container, Provider
is what lets your application components reach into the container.
Let's set this context up for our music application and get our remarkably small playlist stored in there instead!
import React,{ useState } from "react";
//Container
const PlaylistContext = React.createContext();
//Allows access to the container
function PlaylistProvider({ children }) {
const [ playList, setPlayList ] = useState ([
{
id:1,
genre:"Alternative",
title:"You and I",
artist:"Toro y Moi",
track: "/tracks/you_and_i.mp3"
}, {
id:2,
genre:"Rock",
title:"Khuda Bhi Aasman",
artist:"Khruangbin",
track: "/tracks/khuda_bhi_aasman.mp3"
}, {
id:3,
genre:"Rock",
title:"Goodie Bag",
artist:"Still Woozy",
track: "/tracks/goodie_bag.mp3"
}
])
return(
<PlaylistContext.Provider value={{ playList, setPlayList }}>
{children}
</PlaylistContext.Provider>;
)
}
export { PlaylistContext, PlaylistProvider };
Boom.
Step 2: Building the bridge
So far we have only built the storage container and the means to provide access to it. Now we have to connect that means of access into our application.
To do this we simply wrap the parent most component that will house all components that could need access to the context information with our Provider
.
Let's revisit our earlier App
component and see what it looks like now.
import React from 'react';
//This is our means of access
import { PlaylistProvider } from "./user";
function App () {
return(
<PlaylistProvider>
<div className=music-box>
<MusicBox />
</div>
</PlaylistProvider>
)}
export default App
Just like that, any component rendered within MusicBox
or lower can now gain access to our playlist state. Up until now however, we have merely built the bridge from our context to the components that need to access them. We still need to cross the bridge and bring back the information.
Step 3: Crossing the bridge
Okay, so, you find yourself in a component for your music app that is 10 layers deep and now you need a new portion of the playlist info. Let's see how to access that information.
import { useContext } from 'react';
//Bridge crossed
import { UserContext } from "./user";
function Artists () {
//Back across the bridge now with data
const playlist = useContext(PlaylistContext);
const playlistArtists = playlist.map(song => {
return <ArtistTile key={song.id} artist={song.artist} />
}
return(
<div className=playlist-artists>
{playlistArtists}
</div>
)}
export default App
There you have it! Context in use. What a great way to clean up our code base and help to isolate our information down into as few locations as possible! Now, you may be tempted to just use Context
whenever you want to monitor state in your app. Let's explore why this is not such a great choice.
"No need to worry about having to pass props!"
--You, probably, the first time you heard about context
Context Vs. State
Context provides us with abilities very similar to state. Both have the ability to monitor data, and control component re-renders as that data is updated. However, context is something that should, according to the people who wrote it, be used sparingly. This is because while component re-rendering is still possible with context, it is much more intensive on our applications to accomplish.
Top comments (0)