React Hooks: useState

Photo by W.S. Coda on Unsplash

React Hooks: useState

A deep dive into writing functional React code that keeps track of data.

Prerequisite

JSX, JavaScript, JavaScript ES6

Introduction

What was your first thought when you heard about React Hooks? Have you ever wondered whether a real hook and react hooks are related? If we know anything, it's that most programming naming conventions tend to be descriptive of the capability of what they name.

React hooks, therefore, offer a means to latch onto, or if you prefer, hook into React's built-in functionalities. React hook usage has several guidelines, the most important of which is: Hooks should only be used inside React Components. Only React components may utilize hooks; they are not permitted in conditional statements, loops, JavaScript functions, or classes.

The useState hook, one of roughly 15 predefined hooks in React, will be the subject of this tutorial.

What is useState?

React's useState hook lets us monitor a component's state or value. When specific properties are modified, the initial state of that property is updated and the new value is preserved.

Strings, numbers, booleans, arrays, objects, and functions can all be stored in useState. useState allows the use of any data that may be kept in a variable.

To use useState in your React project, you have to import it from react with this syntax:

import { useState } from 'react';

This tells us something about the useState hook: It is a named export from react.

useState hook returns an array that contains two elements, the current value of a property and a function that can be used to update that property.

Consider what I get on the console when I log the useState hook.

import React, { useState } from "react"; 

const App = ()=> {
  const val = useState();
  console.log(val)
}

The result on the console looks like this:

The return value is an array that contains an undefined variable and function.

If I were to pass in a string to useState, its return value would be different.

const App = ()=> {

  const val = useState("Hello World");
  console.log(val)
}

Now the results are different. We have the first element in the array set to the "Hello World" string passed into useState.

That was an interesting dive into the return values of useState in two different conditions. Let’s now look at the syntax for writing useState.

Syntax

We now know that useState returns an array of two elements. So what is the best way to access these elements? We might consider using array indexing to retrieve the contents of the array but that is not the conventional way. The convention is to use an array destructuring assignment to access useState return values.

import React, { useState } from "react";

const App = ()=> {

  const [ val, setVal ] = useState("Hello World");
  console.log(val, setVal)
}

Now, it is clear how these two values are stored, soon we will see how they can be used.

It is important to note that although I stored these values in variables named val and setVal, these are not default variable names. You may decide to name yours milk and chocolate or yin and yang, and everything would work just fine. But it is worth mentioning that by convention, the function name must be prefixed with “set” accompanied by the name of the variable. So if you choose a variable named value then your function would, by convention, be named setValue.

How to use useState Hook: With a variable

What is a basic programming tutorial without a “Hello World” example? This example will show how useState can be used to change some text from a random name to “Hello World”.

import React, { useState } from "react";

// App component
const App = ()=> {
  // Destructuring useState
  const [name, setName] = useState("Joseph Afolabi");

  // Function that handles the click event
  const clicked = ()=>{
    setName("Hello World")
  }

  return (
    <>
      <h1>{name}</h1>
      <button  onClick={()=> clicked()}>Change Name</button>
    </>
  );
}

Explanation:

The code above defines an App component and therein, it sets the values for useState to name and setName.

The default value for the name variable is “Joseph Afolabi”. This is rendered to the DOM.

Also, a click event handler function has been defined as clicked and it uses the setName function to update the string stored in the name variable from what it was to "Hello World".

You might have noticed that I called the clicked function within arrow functions. Why?

The simple explanation is, if it wasn’t done that way, the event listener fires once the page loads. To dive deeper, read here.

Another thing worth noting is how useState was used only within the React component.

Output:

Before the button is clicked, our application looks like this:

Once the button is clicked, the value of the name variable is updated to “Hello World” and simultaneously re-rendered to the DOM.

You might have noticed that I called our clicked function within arrow functions. Why?

The simple explanation is, if it wasn’t done that way, the event listener fires once the page loads. To dive deeper, read here.

Another thing worth noting is how useState was used only within the React component.

How to use useState Hook: With an array

In this example, we will work with an array that contains random numbers. Our goal is to create a number card for each number. We will include a button on each number card that multiplies the number by 2.

First, we write the array that stores the numbers:

const numbersArray = [
  {
    id: 0,
    number: 12,
  },
  {
    id: 1,
    number: 51,
  },
  {
    id: 2,
    number: 47,
  },
  {
    id: 3,
    number: 11,
  }
]

It is important that we include a unique id for each number because in react, every child of an iterable that will be rendered to the DOM must possess a unique key attribute.

Next, we write the App component:

const App = ()=> {
  const [numbers, setNumbers] = useState(numbersArray);
  return (
    <React.Fragment>
      {
        numbers.map((num) => {
          const {id, number} = num;
          return(
            <div key={id}>
                <h3>{number}</h3>
                <button onClick={ () => {multiply(id)} }>Multiply</button>
            </div>
          )
        })
      }
    </React.Fragment>
  );
}

Explanation:

Within the App component, we set the default value of useState to the numbersArray declared earlier.

The array elements are iterated over, data is extracted and rendered to the DOM.

When the button is clicked, we expect a function to carry out the click event and implement the functionality. This function has been called in the button as multiply .

Now, let's define the multiply function

const multiply = (id) => {
    setNumbers(numbers.map((num) => {
      if (num.id == id){
        num.number = num.number * 2;
      }
      return (num)
    }))

Explanation:

This function takes the id of each array element as parameter. We will see why in a moment.

Next, it retrieves it array stored in numbers array in its present state. It iterates over each of the element, checking for the element whose id property matches the id passed to it.

When it finds a match, it multiplies the number by 2, otherwise it return the original value.

Finally, it returns a new array.

Output:

With a little bit of styling, out result looks like this:

If I click the button beside 12 and the button beside 47, the result is shown below:

Both numbers have been doubled.

How to use useState Hook: With an object

Updating the state of the properties in an object is quite easy. Technically, we have used an object in our previous example.

However, consider an example of an object that holds information about an employee. In this object, we would define the name, gender, and image of the employee.

const employee = {
    name: "Udoh Biliki",
    img: "https://res.cloudinary.com/diqqf3eq2/image/upload/v1595959131/person-2_ipcjws.jpg",
    gender: "Male"
}

Next, we would define the Employee component and therein initialize useState with the employee object.

const Employee = () => {
  const [person, setPerson] = useState(employee);


  return (
      <>
        <img src={person.img}></img>
        <div>
            <h3>{person.name}</h3>
            <p>{person.gender}</p>
        </div>
        <button onClick={changeGender}>
          change Message
        </button>
      </>
    )
};

Explanation:

The employee object was set as the default value of useState . Although the object could be defined directly in useState , I think it's better to set it into a variable as we have done.

Next, the object is destructured and its properties are passed into JSX tags to be rendered into the DOM.

Pretty simple, isn't it?

But what happens when you would like to update one of the properties in the object?

For instance, I have set the gender to male but the Image shows that this employee is female. A function has been called to handle that event. It is defined below:

const  changeMessage = ()=> {
    setPerson({...person, gender: "Female"})

    if (person.gender === "Female")
      setPerson({...person, gender: "Male"})
}

Explanation:

This function updates the state of the object, here represented as person.

The change is done on line 2. setPerson updates the object by making a shallow copy of all properties of the person object except the message property that it needs to update.

The …person expression makes a shallow copy of the object using an ES6 syntax called spread operator.

This functionality was implemented with just one line of code. The if statement is just a little bit of extra functionality that lets the button toggle gender to either male or female.

Now you can guess what the result would be when the button is clicked:

You guessed right!

Practice example: Build a class list of students

This article wouldn't be complete without a little task to cement all that has been learned. So here is a simple task to practice with.

Given an array of objects containing basic information about students, build a student list that displays each student’s image, name, and class.

You can choose whatever design you would like to use.

Functionality:

  • You should be able to remove each student from the list

  • You should be able to increment and decrement the class

  • You should be able to remove all students

Hence, you should have a total of three buttons on each student card and one button that clears all students.

This is a fun way to learn to use useState, you will learn a lot on the journey.

Conclusion

This article has provided a mid-level view of useState, yet these are not the most complex use cases. Try your hands on more complex examples and build as many real-life projects as you can.

Happy hacking!