React has improved its state management abilities throughout its journey. With the 16.3 upgrades, the React team introduced Context API to quickly share a state across the entire application or a portion of it. On the other hand, React Hooks came as a new addition to React with version 16.8. React Context API and React useState Hook manage the state in a React application but differ in their use cases and implementations.
So, in this article, I will compare Context API and useState Hook. For better understanding, I have provided various use cases for each.
React Context API is a feature in React that allows data to be passed down through a component tree without having to manually pass props down through every level of the tree. It provides a way to share data between components not directly related to each other in the component hierarchy.
There are situations when multiple components at different nesting levels in a tree need access to the same data. Context lets you broadcast such data across components in a tree, whether at different nested levels or the same nested level. Additionally, it permits these components to modify the data as required.
Although open-source libraries like Redux are helpful when developers want to manage and centralize application states, many developers find Redux somewhat complicated to learn and operate. Alternatively, Context API helps you manage the global states in your React application easily.
To use React Context API, you need to create a context object using the createContext() method. This context object provides two components: Provider and Consumer. The Provider component allows you to wrap the entire component tree with a context object and pass down data as a prop to all the components inside it. The created context object holds the latest value of the state. The context object must be the parent component for all components that want to use the state value. The Consumer component allows you to access the data passed down by the Provider component.
Here’s an example of how to use React Context API.
import React, { createContext, useState } from "react"; const ThemeContext = createContext(); function App() { const [theme, setTheme] = useState("light"); return ( <ThemeContext.Provider value={{ theme, setTheme }}> <MainPage /> </ThemeContext.Provider> ); } function MainPage() { return ( <div> <Header /> <Content /> </div> ); } function Header() { return ( <header> <nav> <ThemeToggler /> </nav> </header> ); } function ThemeToggler() { const { theme, setTheme } = useContext(ThemeContext); return ( <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}> Toggle theme </button> ); }
In the previous example, I imported createContext from the React module. I defined and initialized the ThemeContext object using the createContext function in the following line. The best practice is creating a separate folder outside the components directory to contain all the contexts.
The theme state and setTheme state update functions are passed as the context’s values, and the entire application is wrapped in a ThemeContext.Provider component. Using the useContext Hook pass and the ThemeContext object as the argument, any component within the component tree can access the theme state and the setTheme function.
When creating the context object, we initialize its current state value with defaultValue. The created context object reads values from the closest matching Provider above it in the component tree and stores it as the current context value. See the code snippet below for a better understanding.
import React, { useState, createContext } from 'react'; export const FruitContext = createContext(); export const FruitProvider = (props) => { const fruitList = [ { name: 'Banana', price: '2 USD' }, { name: 'Apple', price: '2.50 USD' }, { name: 'Mango', price: '5 USD' } ] return( <FruitContext.Provider value = { fruitList } > {props.children} </FruitContext.Provider> ); }
In the example, fruitList is a list of fruit objects we have created. All children wrapped from the FruitContext Context Provider can receive the fruitList value.
Context API has two different ways to subscribe to the state values.
The first way to subscribe to context changes is to use the Context.Consumer property in a function component. The consumer requires a function as a child to receive the current context value.
import { FruitContext } from '../Contexts/FruitContext'; function Fruits() { return ( <FruitContext.Consumer> {value => <><h1>{value[0].name}</h1> <h3>{value[0].price}</h3></> } </FruitContext.Consumer> ) }
On top of the above code, we have imported the created context API. The attributes inside FruitContext.Consumer can consume the latest value of the state. The value contains the list of fruits we have passed. The returning output is below.
With the advent of React Hooks, there is another way to get context values. The nested components can now consume Context with the useContext Hook.
import React, { useContext } from 'react'; import { FruitContext } from '../Contexts/FruitContext'; const FruitBacket = () => { const fruitList= useContext(FruitContext); return ( <div> {fruitList.map(fruit => ( <div> <h1>Name: {fruit.name}</h1> <h3>Price: {fruit.price}</h3> </div> ))} </div> ); };
In the nested component (FruitBacket), import the useContext Hook and the Context object we have created. Inside a functional component, useContext can be called like useState to get the state values. The above code snippets output the following.
As we discussed, avoiding prop drilling (passing data to the child or nested components) is the best achievement of Context API. It will keep your code cleaner and easy to work.
Here are some scenarios where using React Context API can be beneficial:
The useState function helps manage state(local state) variables within a React functional component. You are responsible for the initial value of the state, and the useState function returns the current value along with a method to modify it.
On the top of the file, import useState Hook from React. In functional components, replace this.state of the class components with useState. Then you can directly call useState inside the functional component.
import React, { useState } from 'react'; const AddFruitToBucket = () => { const [name, setName] = useState(''); const [price, setPrice] = useState(0); return ( {/* return code */} ); }; export default AddFruitToBucket;
The previous code created the current state and a method to change the current state for name and price variables.
It’s easy to get the values of states defined using useState. However, if you want to call them inside HTML tags, you must wrap them in curly braces.
const Fruit = () => { const [name, setName] = useState(''); const [price, setPrice] = useState(0); return ( <div> <h1>Name: {name}</h1> <h3>Price: {price}</h3> </div> ); };
I have mentioned that useState returns a pair of values when it declares a state. The second variable is a function to update the current value of the state. Using that, you can easily modify the state value.
function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
The previous example demonstrates a simple example of changing the value of the count state using the useState Hook.
Present-day developers prefer using React functional components since they’re simpler than class components. In addition, the useState Hook allows you to add stateful behavior to functional components without the need for class components. Here are some scenarios where using React useState Hook can be beneficial:
We can create a toggle flag in a functional component using useState Hook. The following code explains how you can turn a toggle on or off using useState Hook.
import React, { useState } from 'react' const FruitBucket = () => { const [toggle, setToggle] = useState(false) return( <><button onClick={() => setToggle(!toggle)}>Add to Cart!</button></> ) }
You may utilize a state to store the fetch() response from the API when using APIs and the spinner’s state to show if data is being fetched. The following code is an example of that. isLoading is used to indicate the data state. Once the data is fully retrieved, “Data loaded!” will show.
const ShowPlanets = props => { const [isLoading, setIsLoading] = useState(false); const loadPlanets = async () => { setIsLoading(true); const response = await fetch('https://swapi.dev/api/planets'); const planets = await response.json(); console.log(JSON.stringify(planets, null, "\t")); setIsLoading(false); }; return ( <> {isLoading? ( <div><p>Data is loading...</p></div> ) : ( <div><p>Data loaded!</p></div> )} </> ); };
Throughout this article, I have discussed the main differences between React useState Hook and React Context API. React Context API shares data across components, while useState Hook manages the state within a single component.
Also, I have shown you the best way to use these React features in different scenarios. I hope this article guides you to get the most out of useState Hook and Context API.
Thank you for reading!
The Syncfusion Essential Studio® for React suite offers over 70 high-performance, lightweight, modular, and responsive UI components in a single package. It’s the only suite you’ll need to construct a complete app.
If you have questions, contact us through our support forum, support portal, or feedback portal. We are always happy to assist you!