So it’s not exactly news on the interwebs that React Context will be stable in the upcoming release of React, React 16.3.0. React Context is/was an experimental feature of React that received better implementation thanks to multiple PRs from contributors.
But even though it’s not news, it is worth taking a minute to learn when and how to use it. And that’s what we’ll do here.
React Context is a way for a child component to access a value in a parent component.
One familiar problem in React is what is popularly known as prop drilling.
Prop drilling occurs in situations where you’re looking to get the state from the top of your react tree to the bottom and you end up passing props through components that do not necessarily need them.
React Context solves the problem of props drilling. It allows you to share props or state with an indirect child or parent.
Ordinarily, you’d use a state management library like Redux or Mobx, but what if you don’t want to? Or the data to be passed is so minute that using a state management library would be overkill.
This is where the new React Context API comes in.
Well, the current Context API is considered to be unstable as it an experimental API and is likely to break in future releases of React. Also, the current and soon to be old Context API had issues with shouldComponentUpdate blocking context changes.
This happens when an intermediate component updates using shouldComponentUpdate, React will automatically assume there are no changes and reuse the entire subtree. The problem with that is if the subtree contains a context consumer, the consumer will not receive the latest context.
With the new React Context API, you should be familiar with three things:
React.createContext
React.createContext is used to initialise the Context and it’s passed the initial value. It returns an object with a Provider and a Consumer. Providers and consumers come in pairs, that is, for each provider, there is a corresponding consumer.
const Context = React.createContext();
Provider
The Provider component is used higher in the tree and accepts a prop called value.It provides a root upon which any child in that tree can access the values that are provided by that context provider.
render() { return ( \<Provider value={this.state.contextValue}\> {this.props.children} \</Provider\> ); }
The provider accepts a context value as a prop. Any matching consumer in the provider’s subtree can access it, regardless of how deeply it’s nested.
Consumer
The Consumer as the name suggests consumes the data being passed and renders the data by using a render prop API.
render() { return ( \<Consumer\> {contextValue =\> \<Child arbitraryProp={contextValue} /\>} \</Consumer\> ) }
Before I demonstrate how React Context can be used, let’s take a look at this example below that uses prop drilling to get some data, so that we can establish a bit of…wait for it…context.
Classic Prop Drilling
Classic Prop Drilling by yomete using react, react-dom, react-scripts
The code block above is just a short example of how prop drilling works. We had to pass the props data from the top to the bottom even though the components don’t necessarily need it.
Now let’s see how this can be solved using React Context. To get started, you have to install the alpha version of React 16.3.
npm i react@next react-dom@next
React Context
React Context by yomete using react, react-dom, react-scripts
Let’s go through the code block above.
const JediContext = React.createContext();
We first initialise a new Context and set it to a const variable. It can be set as anything.
class JediProvider extends Component { state = { name: "Vader", side: "dark" }; render() { return ( \<JediContext.Provider value={{ state: this.state, turnGood: () =\> this.setState({ side: "good" }) }} \> {this.props.children} \</JediContext.Provider\> ); } }
Inside the JediProvider Component, we create a Provider Context inside the render() function as JediContext.Provider . JediContext.Provider accepts a prop called value which is set to the state and a function that updates the state.
Now that we have a Provider, how do we consume the data, even across parent to child components?
class App extends Component { render() { return ( \<JediProvider\> \<Vader /\> \</JediProvider\> ); } }
const Vader = props =\> { return \<Luke /\>; }; const Luke = props =\> { return \<KyloRen /\>; };
const KyloRen = props =\> { return ( \<JediContext.Consumer\> {context =\> ( \<React.Fragment\> \<p\>My grandfather is {context.state.name} \</p\> \<p\>He belonged to the {context.state.side} side\</p\> \<button onClick={context.turnGood}\>Turn\</button\> \</React.Fragment\> )} \</JediContext.Consumer\> ); };
render(\<App /\>, document.getElementById("root"));
The first thing to do would be to wrap all the children of the top level App component with the provider we created, which is, JediProvider . That means all of its children will have access to the value in the Context.
Almost like the prop drilling method above, the components are passed into each other albeit with no props, no drilling. The difference is in the bottom child which is KyloRen . The KyloRen component uses JediContext.Consumer to pass context as a render prop. Since context holds the state , you can easily use it to display the data that is required.
As seen with context.turnGood , we can also use Context to change and update the state. Whenever the button is clicked, it calls the turnGood function that was added as a value in the Provider.
Overall, this is a better solution than the prop drilling methods, you don’t have to pass props unnecessarily to component who don’t need them.
React Context
React Context by yomete using react, react-dom, react-scripts
No. The new Context API has its limitations.
For example, it encourages the use of immutable or persistent data structures or strict comparison of context values which might prove difficult because many common data sources rely on mutation.
Another limitation is that the new Context API only allows for a Consumer to read values from a single Provider type, one consumer to one provider. Unlike the current API, which allows a Consumer to connect to multiple Providers.
You can use it when all you want is simple state management, or in cases where you want to pass some props deeply without the overkill that comes with Redux or MobX.
The Context API should be updated and included in the next couple of updates for React. It will definitely be interesting to see the updates and feature changes for the Context API going forward.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.