Complete beginner's guide on the useState Hooks 🕛>🦸‍♀️

Complete beginner's guide on the useState Hooks 🕛>🦸‍♀️

Introduction

useState is one of the most basic and widely used in-built React Hook. These are simple JavaScript functions that help with state management in the functional component. And just like any other javascript function, call Hooks in the same way function_name(arguments) 😳

In controlled components, value is changed or updated by the state, uncontrolled by event handlers, and re-rendered on the state or props change. useState helps to preserve the state between re-renders and adds a React state to the functional components.

This article serves as a complete guide to the useState Hook in React, the equivalent of this.state/this.setState for functional components.

What is a useState Hook? 🤷‍♀️

Here is a simple, straightforward definition  - It allows state management in functional components.

These are simple functions that allow functional components to "hook into" React state and lifecycle features.

Note: Hooks don't work inside classes  -  they use React without classes.

When to use it?

Variables normally disappear when the function exits. useState is the solution for preserving the state of a variable after its life cycle, i.e., React will maintain the state between re-renders.

Syntax 📝

Start by importing the useState and then declaring it at the top of the component. useState accepts an initial state and returns an array with ✌ values:

  • One state variable which holds the value (the state), and
  • An async function to update the value of the state variable.
import React, { useState } from "react";

const getExistingColors = () => {                                 
   const colorState = useState("red"); // Declaration
   const color = colorState[0]; // Returns the value red
   const setColor = colorState[1]; // Returns function to update the value
}

Here is the link for the demo

Note: The useState Hook declares only one state variable (of any type) at a time.

What do the square brackets mean? It helps access the values by colorState[0] and colorState[1]. This might lead to confusion. For this reason, Array Destructuring is preferred.

Destructuring has made extracting data from an array very simple and readable. Imagine how time-consuming it would be to attempt to extract data from a nested array with five or six levels. Instead, use an array literal on the assignment's left side.

const rainbow = ["orange", "yellow", "green", "blue"];
const [orange, yellow, , blue, red] = rainbow;

console.log(orange) // Output: orange
console.log(yellow) // Output: yellow
console.log(blue)   // Output: blue
console.log(red)    // Output: undefined

Here is the link for the demo

In simpler terms, destructor the useState Hook's returned value.

The signature for the useState Hook is as follows:

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

Two things to notice here: one is the argument to the Hook, and the other is the return value. The initial state argument is used only during the first render, which can store an Array, Object, String, Number, Boolean, or Null.

Note: The useState Hook returns an array because it is more flexible and easy to use. If the returned type was an object having a fixed set of properties, it wouldn't be able to rename the variables so easily.

How to use it? 🗞

To update the state, call the function setState with a new state value setState(newStateValue) The state cannot be directly mutated. To change the state value, call the returned function to update it because the state is considered immutable. 😯

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

color = "green";     // Doesn't change the value of color
setColor("green");   // The color state holds the new value green
setColor("green");   // Does not re-render again

Here is the link for the demo

In the above example, notice that the setColor doesn't merge the old and the new value, rather it replaces with the new value provided.

Note: The subsequent re-render is skipped if the updater function returns the same value as the current state.

The implementation

Now that the fundamentals of the useState are obvious let's examine various use cases of the Hook 👀

Multiple Hooks handling 🤹‍♀️

The code snippet shows two different calls to update the Hooks.

const [phoneColor, setPhoneColor] = useState("black");
const [headsetColor, setHeadSetColor] = useState("red");

function updateTheColorsToBuy() {
   setPhoneColor("blue");
   setHeadSetColor("purple");
}

Here is the link for the demo

React will only generate one re-render even though two updater functions are called one after the other to maintain good performance by avoiding too many inefficient re-rendering.

When the updateTheColorsToBuy function is called, React batches the state updates and re-renders at the end where all the state gets updated.

What would happen if React did not support batch updates and re-rendered every time a Hook was to get updated? It would cause severe performance issues, increase load times, or cause the application to crash.

Note: Re-rendering is an expensive operation; synchronizing state updates would affect the application's health.

New state based on the existing state

When the new state is dependent on the existing state value - for example, a computation or value - use the functional state update.

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

setColor((prevColor) => {
  return "light" + prevColor;
});

Here is the link for the demo

Pass a functional argument to setColor. Internally, React will invoke this function with the existing state as the argument, and the returned value from this function is set as the new state. Since setColor is async, React guarantees that the previous color is accurate.

Initialize state from function 😎

When the Hook has to be initialized from a function, it can be done the following way.

const [color] = useState(() => {
   let tempColor = window.localStorage.getItem("color");
   return tempColor || "blue";
});

Here is the link for the demo

Since initialization happens only on the first render, if the initial state results from an expensive computation, pass a function, which will be invoked only on the initial render, this is called lazily initializing the state.

const [color, setColor] = useState(
  () => yourExpensiveColorComputation(props)
)

Avoiding Dependency

The functional updater helps us avoid dependencies over useEffect, useMemo, or useCallback. But there are use cases when we may want to.

const [phoneColor, setPhoneColor] = useState("black");
const [headsetColor, setHeadSetColor] = useState("red");

// add dependencies to prevent ESLint warning
  useEffect(() => {
    setPhoneColor("red");
  }, []);

// Runs the callback only on mount
  useEffect(() => {
    setHeadSetColor("black");
  }, [setHeadSetColor]);

Here is the link for the demo

UseState With Objects ✨

A Hook that contains an object cannot be updated directly. If the functional updater is called and the arguments are passed directly, the values in the Hook will not merge but get replaced. To merge, either use a spread operator (…), put it in a new object, or with Object.assign()

const [buyColors, setBuyColors] = useState(
  { paints: 19, crayons: 4 }
);

setBuyColors({ paints: 25 });  
// This will result in replacing the values of buyColors
// buyColors will now hold => { paints: 25 }

setBuyColors({
 ...buyColors,
 paints: 25
})
// Whereas, this will result in updating the buyColors Hook
// buyColors will now hold => { paints: 25, , crayons: 4 }

Here is the link for the demo

To see how an array of objects works with the Hook, check out the demo.

Conclusion

The interesting part of useState is that it is nothing more than an array declaration under the hood. When used, it returns two array elements, the first of which is the variable and the second of which is the function to update the variable.

Practicing with this Hook and building projects with it will help you learn it faster.

Thank you for reading this article 😊 In the next article, some advanced topics, rules to follow, and mistakes to avoid when using this hook will be presented. Stay tuned, and cheers!

Please feel free to ask questions in the comments below.