React Hook (useState) with React Context API
Damola Adekoya
Posted on May 29, 2019
Managing State in React App is tricky, especially when you want to share data from component 1 to (x)components, you will have to pass the data from component 1, 2 to 7 which is basically unnecessary. Packages like Redux, Flux, MobX e.tc help to manage states but the problem with this kind of packages they are external to react library, some people find it kind of cumbersome to fully understand, working and integrating it with react. So react team release alternative to Redux which is Context API in React 16.x.
What is React Context API
It is a feature/approach that provides a way to pass data through the component tree without having to pass props down manually at every level.
Let assume I am a delivery man, I want to deliver a package to Floor 7 of a particular building using staircase. For me to get to floor 7, I have to carry the package from floor 1 to 2 to 3..7, it kinda cumbersome right?, why can’t I just jump/teleport myself from floor 1 to 7 and vice-versa without me stressing myself of going through from floor 1 of a building to the last floor(7th Floor). That’s typically how passing data via props from parent component to nested child. But with React Context you don’t have to do go through all that, all you need to do is just jump directly to floor 7 and jump back without making any additional/unnecessary trip.
Simple Guide
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
Terminologies to understand
React.createContext
const MyContext = React.createContext(defaultValue);
it will allows you to create a context object, which accept a default values. When React renders a component that subscribes to this Context object it will read the current context value from the closest matching Provider above it in the tree.
Context.Provider
<MyContext.Provider value={/* some value */}>
The moment a context object has been created you will have access to context API method such as Provider, Consumer e.t.c. Provider allows consuming component to subscribe to context changes and also set/store value/data.
Context.Consumer
<MyContext.Consumer>
{value => /* render something based on the context value */}
Just as the name implies Consumer it allows you to consume the value set/store from the Context Provider.
When Should I use React Context API
Context API is only meant to be used when you need to access your data from a nested component, The moment you realised the nested level of your component is more than 3, then try to use Context API to avoid passing unnecessary data via props.
React Hook(useState)
Sophie Alpert said at React Conf 2018 Class are very confusing and annoying to understand for both Human and Machine. React team develop React Hook to manage state from a stateless/functional component and also handle every component life cycle supported by class component. e.g componentDidMount, componentDidUpdate,componentWillUnmount e.t.c
Simple Guide
import React, { useState } from 'react';
const hookExample =()=>{
const[state, setState] =useState(0);
return (
<div>
<p>You clicked {count} times </p>
<button onClick={()=> setState()}>Click me </button>
</div>
}
const [state, setState] = useState(0);
I used array destructing to extract state and setState from useState. I set the initial value/state as zero(0). useState can accept any default value string,numbers,bool,array,object.
NB: useState return an array of 2 elements: current state and set State. (state and setState) are just variable representation, you can use any name to represent your state and setState.
Passing data via props
import React, { Fragment } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
//Building Component
const Building = () => {
const data = {
companyName: "DHL Delivery Package",
recipientName: "Mr Adekoya Damola Felix",
package: "MacBook Pro retina Display (20Kg)"
};
return <Floor1 data={data} />;
};
//Floor1 Component
const Floor1 = ({ data }) => {
return <Floor2 data={data} />;
};
//Floor2 Component
const Floor2 = ({ data }) => {
return <Floor3 data={data} />;
};
//Floor3 Component
const Floor3 = ({ data }) => {
return <Floor7 data={data} />;
};
//Floor 7 Component
const Floor7 = ({ data }) => {
return (
<Fragment>
<h3>Welcome to Floor 7</h3>
<p>
<strong>Company Name: </strong>
{data.companyName}
</p>
<p>
<strong>Recipient Name: </strong>
{data.recipientName}
</p>
<p>
<strong>Package: </strong>
{data.package}
</p>
</Fragment>
);
};
function App() {
return (
<div className="App">
<h1>Simple Context API Tutorial using Hook(useState)</h1>
<Building />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
From above source code is the normal way of passing data without Context API or redux in react. In order for component 7 to received data (package), the package has to pass all the way from Floor 1 to Floor 2… Floor 7.
Managing State with hook and context API
import React from "react";
export default React.createContext();
in the above snippet I created a reusable context that can be use across other component in my application.
import React, { useState } from "react";
import PackageContext from "./context";
const provider = props => {
const [state, setState] = useState({
companyName: "DHL Delivery Package",
recipientName: "Mr Adekoya Damola Felix",
package: "MacBook Pro retina Display (20Kg)",
deliveryStatus: "Delivery In Progress..."
});
return (
<PackageContext.Provider
value={{
data: state,
updateDeliveryStatus: () => {
setState({ ...state, deliveryStatus: "Delivered" });
}
}}
>
{props.children}
</PackageContext.Provider>
);
};
export default provider;
I import context component created earlier, we are going to be using it to set our provider value and consume the value.
const [state, setState] = useState({
companyName: "DHL Delivery Package",
recipientName: "Mr Adekoya Damola Felix",
package: "MacBook Pro retina Display (20Kg)"
});
I set the initial state which is the package(data) to be delivered, you can consider provider component as the centre store and also HOC(Higher order component which going to wrap our main component.
return (
{props.children}
);
I returned my context component i created earlier, assign a value as a props which hold the state data (Package Details)
import React, { Fragment } from "react";
import ReactDOM from "react-dom";
import Provider from "./provider";
import Context from "./context";
import "./styles.css";
const Building = () => {
return <Floor1 />;
};
//Floor1 Component
const Floor1 = () => {
return <Floor2 />;
};
//Floor2 Component
const Floor2 = () => {
return <Floor3 />;
};
//Floor3 Component
const Floor3 = () => {
return <Floor7 />;
};
//Floor 7 Component
const Floor7 = () => {
return (
<Context.Consumer>
{context => (
<Fragment>
<h3>Welcome to Floor 7</h3>
<p>
<strong>Company Name: </strong>
{context.data.companyName}
</p>
<p>
<strong>Recipient Name: </strong>
{context.data.recipientName}
</p>
<p>
<strong>Package: </strong>
{context.data.package}
</p>
<p>
<strong>Delivery Status: </strong>
{context.data.deliveryStatus}
</p>
<button onClick={context.updateDeliveryStatus}>
Update Delivery Status
</button>
</Fragment>
)}
</Context.Consumer>
);
};
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Provider>
<Building />
</Provider>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Let go straight direct to last component (Floor 7) where we are going to use our data.. I wrapped all my tags with which allows me to connect to my store and access my data directly.
NB: In any component you want to use or consume the data you already saved in the provider. all you have to do is import your context component and use the Consumer property or you can destructure Consumer from the context component. E.g
import {Consumer} from './context-component'
From your App Component you need to wrap main component(Building component) as a parent component. Every Child/children component of the building will have access to the Provider Data more like Provider in redux.
How to Update My State from Child or Consumer Component
Updating your state that resides in component 1 from component 7 might look strange, but you can actually update your state from any Consumer component.
{
data: state,
updateDeliveryStatus: () =>
{
setState({ ...state, deliveryStatus: "Delivered" });
}
}}>
in the Provider value data object, you need to set the action you want to perform in the object as a function which can later be triggered in the consumer component.
Conclusion
In this article I hope I was able to impact and explain how context api and react hook usestate works. Context API is basically your center store where you can store your data that you want to access globally in your react application. Hook allows you to manage your state in a functional component.
I have created a simple demo on codesandbox to demonstrate how to use context API with react hook useState.
originally published on https://codeburst.io/beginner-guide-to-react-context-api-with-hook-usestate-4df7c3e08ad
Posted on May 29, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.