React useState Vs. Context API: When to Use Them

Jollen Moyani - Jun 8 '23 - - Dev Community

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.

What is Context API?

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.

How does Context API work?

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

Provide a state value to Context API

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

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.

Subscribe state values from Context API

Context API has two different ways to subscribe to the state values.

Option 01

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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. Context.Consumer property output

Option 02

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

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. useContext Hook Output

When to use Context API

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:

  • Global data: If you have data that needs to be accessed by multiple components throughout your application, such as the current user object, theme, or localization settings, you can use the React Context API to share this data across your components.
  • Avoiding prop drilling: If you pass props down multiple levels of the component tree, especially when the intermediate components do not directly use the props, it might be a good idea to use React Context API to avoid prop drilling and simplify your component code.
  • Large-scale applications: If you’re building a large-scale application with many components and complex data flows, React Context API can help you manage your state and data more efficiently and make your code more maintainable.
  • Cross-cutting concerns: If you have cross-cutting concerns in your application, such as error handling or authentication, that need to be applied across multiple components, using React Context API can make managing these concerns easier and keeps your code organized.

What is useState Hook?

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.

How does useState work?

Declaring useState

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;
Enter fullscreen mode Exit fullscreen mode

The previous code created the current state and a method to change the current state for name and price variables.

Calling useState

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

Modify a state value using useState

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>
  );
}

Enter fullscreen mode Exit fullscreen mode

The previous example demonstrates a simple example of changing the value of the count state using the useState Hook.

When to use UseState

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:

  • Form inputs: If your component has form inputs, use the useState Hook to manage their state. This allows you to keep track of user input and update the UI accordingly.
  • Conditional rendering: If you have components that need to change their state based on user actions or some other condition, use the useState Hook to manage the component’s state and re-render the component when the state changes.
  • UI components: If UI components need to change their state based on user interaction, such as dropdown menus, tabs, or accordions, use the useState Hook to manage the component’s state and update the UI accordingly.
  • Toggling: If you have components that need to toggle between two or more states, such as a modal or a tooltip, use the useState hook to manage the component’s state and toggle between the different states.

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></>
  )
}

Enter fullscreen mode Exit fullscreen mode

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>
     )}
    </>
  );
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

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!

Related blogs

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .