Mastering useState: Exploring State Management in React

If you’re new in the world of React development, you might have encountered instances where you need to have state variables in functional components.

In such a scenario using the useState() hook can be of great aid for efficient project completion. With useState you can easily store state variables in functional components of your React project for enhanced performance.

This blog will help you understand in-depth about useState hook and help you implement them in real-time scenarios with ease as well.

Table of contents:

  1. Introduction to useState React Hook
  2. Getting started with implementation of the useState in React
  3. How to use the useState React hook to update state?
  4. Methods to use Object as State variable in useState React hook
  5. useReducer hook

#1 Introduction to useState React Hook

React Hooks are basically functions that help developers in adding in state variables to function components in order to effectively direct the lifecycle methods within the class. When we implement React.useState hook within a component it assists developers in generating a singular state associated with the function component.

The useState hook can be extensively used for the local component states. However, if you’re working on large-scale development projects, using just the useState hook might not be enough and you’ll have to eventually explore additional state management solutions.

Now that you have a clear idea about useState React hooks, let’s dive head-first into its implementation with the in-detail tutorial below.

#2 Getting started with implementation of the useState in React

Importing the useState hook in your React project is pretty easy. Just follow the syntax mentioned below to get started in an instant:

import React, { useState } from 'react';

However, in contrary to the state objects that effectively allow developers to declare multiple state variables while using useState as mentioned above you can only declare a single state and directly includes the value of the state variable in the form of an argument  as mentioned in the example below:

const [state, setState] = useState(initialValue);

// example

const [color, setColor] = useState("Blue");

To initialize useState as a function

Further, if the initial state is the result of an expensive computation process, you can lazily initialize the functions as given in the example. In this, the initial value will be assigned during the process of the initial render and during the subsequent renders the argument in the useState hook would be put aside and the current value will be retrieved effectively.

const Text = () => {
   const textState = useState( () => expensiveComputation() );
   /* ... */
}

useState returning an array

When you use useState for functions where the first variable is a state variable and the second one updates the value of the variable it usually returns an array.

Here is an example of useState returning an array in a React functional component:

import React, { useState } from 'react';

function increment() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('John');

  return (
    <div>
      <p>{count}</p>
      <p>{name}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setName('Jane')}>Change Name</button>
    </div>
  );
}
useState returning an array
useState returning an array

In this example, useState is called twice: once to create the count state variable and its update function, and again to create the name state variable and its update function. Both useState calls return an array with two elements: the current value of the state variable, and a function to update it.

The count and name state variables are then used in the component to display their current values, and the update functions are used in the buttons to increment the count or change the name when the buttons are clicked.

By returning an array from useState, it is possible to have multiple state variables and update functions in a single functional component. This can be useful for managing complex state in a React application.

However, if the useState returns an object in the case of an array assigning custom names in such cases can become extremely complicated. In such scenarios you can alternatively use the useState hook as mentioned below:

// Without using object destructuring
const textState = useState( '' );
const text = textState.state;
const setText = textState

// Using object destructuring
const { state: text, setState: setText } = useState( '' );
const { state: list, setState: setList } = useState( [] );

Here we are assuming the properties of the object to be the set and setState.

#3 How to use the useState React hook to update state?

Now that you are fairly aware of how you can effectively use the useState React Hook let’s get into a bit more complicated aspects of it. One of the most common real-time use is to update the state variable using the useState React Hook.

import React, { useState } from 'react';

function update() {

  const [user, setUser] = useState({
    name: 'John',
    email: 'john@example.com',
  });

  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Email: {user.email}</p>
      <button onClick={() => setUser({ ...user, name: 'Jane' })}>
        Change Name
      </button>
    </div>
  );
}

In the above example, useState is called with an initial value of an object for the user state variable. This call to useState returns an array with two elements: the current value of user, which is an object with the properties name and email, and a function to update the value of user.

The current value of user is displayed in the component, and the update function is used in the button to change the value of user.name to 'Jane' when the button is clicked.

To update the state using the useState hook, you simply call the update function that is returned by useState, passing in the new value for the state variable. In this example, the update function is called in the button's onClick event handler with the new value for user, which is a new object that is created by spreading the existing user object and then setting the name property to 'Jane'. This updates the value of user in the component's state, and the new value is displayed in the component.

Update state using useState React hook
Update state using useState React hook

Overall, to use the useState hook to update state, you call useState to create a state variable and its update function, and then call the update function with the new value for the state variable whenever you want to update the state.

Learn about React Hook Forms in this blog post - How to use React Hook Form for Crafting Interactive Forms?

#4 Methods to use Object as State variable in useState React hook

In React, it is possible to use an object as a state variable with the useState hook. To use an object as a state variable, you can pass the initial object to the useState hook as follows:

const [state, setState] = useState({ /* initial object */ });

1. Spread operator (...)

Use the spread operator (...) to spread the properties of the existing state object into a new object, and then update the desired properties. For example:

setState({ ...state, name: "Jane" });

2. Object.assign()

Use the Object.assign() method to create a new object with the updated properties. For example:

setState(Object.assign({}, state, { name: "Jane" })); 

3. setState() function

Use the setState() function to directly update the properties of the state object. For example:

setState(prevState => ({
  ...prevState,
  name: "Jane"
}));

4. Functional form of setState()

Use the functional form of setState() to update the state object. In this method, you pass a function to setState() which receives the previous state as an argument and returns an updated state object. For example:

setState(prevState => {
  return {
    ...prevState,
    name: "Jane"
  };
});

Overall, each of these methods can be used to update an object as a state variable with the useState hook in React. The specific method you choose will depend on your personal preferences and the needs of your project.

Ways to effectively update State in Nested Object

A nested object allows the React developers to store the state variables in one place effectively for better functionality. However, when you work with nested objects, the Object.assign and spread syntax provides a shallow copy rather than offering a deep copy.

To solve this, let us take the following example and try to update the text property:

const [textObj, setText] = useState({
  author: '',
  text: {
    id: 1,
    text: ''
  }
});

For this, let’s start by copying the fields of the original object  to a new object:

// Correct

setText(prevState => ({
  ...prevState,           // copy all other field/objects
  text: {                 // recreate the object that contains the field to update
    ...prevState.text,    // copy all the fields of the object
    text: 'My text'       // overwrite the value of the field to update
  }
}));

In a similar fashion, you can proceed to update other fields as well in case you’re working on nested objects.

#5 useReducer Hook

The useReducer hook is a hook in React that allows you to manage state in your functional components. It is similar to the useState hook, but it provides more powerful capabilities for managing state.

The useReducer hook takes two arguments: a reducer function and an initial state value. The reducer function is a pure function that takes the current state and an action, and returns a new state. The initial state value is the state that the reducer function will use when the component is first rendered.

Here is an example of how you might use the useReducer hook:

import React, { useReducer } from 'react';

function todoReducer(state, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, action.todo]
      };
    case 'REMOVE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.id)
      };
    default:
      return state;
  }
}

function TodoList() {
  const [state, dispatch] = useReducer(todoReducer, { todos: [] });

  const addTodo = todo => dispatch({ type: 'ADD_TODO', todo });
  const removeTodo = id => dispatch({ type: 'REMOVE_TODO', id });

  return (
    <div>
      {state.todos.map(todo => (
        <Todo key={todo.id} todo={todo} onRemove={removeTodo} />
      ))}
      <AddTodoForm onAdd={addTodo} />
    </div>
  );
}

The major difference between useReducer and useState in react is that in useState you can invoke the function of state updater whereas in useReducer developers invoke dispatch to pass it an action.

Final Words on useState React Hooks

useState in React hooks can be a game changer for including state variables within the functional components. The useState can help you update the state within an array, use an object as a state variable, and update the state within nested state objects as well.

This allows you to access an even greater number of functionalities for your React projects effectively. So, follow the tutorials mentioned above today to make the best out of useState react hooks while working on React project.


React Performance Monitoring with Atatus RUM

Visualize React errors and performance issues influencing your end-user experience. With React performance monitoring of Atatus you can identify and resolve problems faster with in-depth data points that assist you in analyzing and resolving them.

Using Atatus, you can get an overview of how your customers experience your React app. Learn how to identify the root cause of a slow load time, route change, and more on the front end by identifying performance bottlenecks in the front end.

React Error and Performance Monitoring of Atatus
React Error and Performance Monitoring of Atatus

Ensure that your React app sends all XHR requests. Monitoring and measuring the response times and failure rates of XHR calls. Optimize AJAX request performance by identifying slow and failed calls. Analyze AJAX calls in real time based on the browser, the user, the version, the tag, and other attributes.

Identify the reasons behind bad front-end performance and slow page loading that are impacting your customers. Inspect individual users who are experiencing poor performance because of slow pages, React exceptions or a failing AJAX method.

Identify how your end users' experience is impacted by the network or geography. In addition to identifying slow React assets, long load times, and errors in sessions, session traces provide a waterfall view of the session.

Take a closer look at your React app performance with Atatus. To get started activate Atatus free-trial  for 14 days!

Vaishnavi

Vaishnavi

CMO at Atatus.
Chennai

Monitor your entire software stack

Gain end-to-end visibility of every business transaction and see how each layer of your software stack affects your customer experience.