loading...
Cover image for How to use the State Hook in React

How to use the State Hook in React

damcosset profile image Damien Cosset Originally published at damiencosset.com Updated on ・5 min read

Introduction

Hooks have been introduced in React 16.8. This feature completely changed the way we write our components.
As you may know, before Hooks, you couldn't use state in functional components. Whenever you needed to add state to a functional component, you needed to re-write that component as a class... Annoying. The state Hook finally solves that issue.

How to use it

The state hook is used as follow:

import React, {useState} from 'react';

const CreateDebate = () => {
    // We declare a state variable, called debateName
    const [debateName, setDebateName] = useState("");

    return (
        <div>
            <p>Enter a debate name:</p>
            <input type="text"
            value={debateName}
            onChange={e => setDebateName(e.target.value)}>
        </div>
    )
}

We have here a very simple piece of code. We create a functional component called CreateDebate. That component renders a div element. Inside that element, we find a paragraph with an input. We use the Hook state, with useState, to add state to this component. We'll go in detail on how it works, but for now, let's compare that with the same functionality with a class component.

import React from "react";

export default class CreateDebateClass extends React.Component {
  constructor() {
    this.state = {
      debateName: ""
    };
  }

  render() {
    return (
      <div>
        <p>Enter a debate name:</p>
        <input
          type="text"
          value={this.state.debateName}
          onChange={e => this.setState({ debateName: e.target.value })}
        />
      </div>
    );
  }
}
Function components

React Hooks do not work with classes. They only work with functions. As a reminder, function components can be written in different ways:

function MyFunctionComponent() {
  //Hooks go here
  return <div />;
}
const MyFunctionComponent = () => {
  //Hooks go here
  return <div />;
};

The React team recommends the term Function components to talk about these functions. Before Hooks, you may know them as Stateless components or Functional components.

A hook in detail

What is a Hook exactly? A Hook is a special function. It allows you to use certain React features. Our first example details the state Hook. When using that hook, we can use the state feature you are used to see in class components.

To use that particular hook, you first need to import it.

import React, { useState } from "react";

const StatefulFunction = () => {
  // Your code...
};

Before, when you had to use state inside a component, that component had to be a class. Now, we can simply import the useState function for that!

For those who don't know, or remember, state is a React feature. It allows you to keep variables values between function calls. Usually, when a function is done with its job, the variables disappear. Which would cause some problem every time we need to update our interface and re-render our components. With a state, we can keep and update variables over time without losing our progress.

const Example = () => {
  const [firstName, setFirstName] = useState("Damien");
  return <div />;
};

The useState function declares a new state variable. The function is a new way to use this.state you found in classes. As you can see above, useState takes one argument. This argument represent the initial state. In a class, that would be done in the constructor. In a class however, that initial state needs to be an object, even if you only have one string or integer in your state.

class Example extends React.Component {
  constructor() {
    this.state = {
      firstName: "Damien"
    };
  }

  render() {
    //...
  }
}

With useState, that can be anything you want. Here, I want my initial state to be a string.

What does it return? What's up with that syntax?
const [firstName, setFirstName] = useState("Damien");

The useState function returns two values. The first value is the current state, the second is the function that updates that state. In our case, firstName is the current state, and setFirstName is a function that will allow me to modify the state value.

The syntax might seem a bit odd if you are not used to it. This is what we call array destructuring, a cool little syntax feature we got from Javascript ES6.
This syntax allows us to assign the first item in the array to a variable, here called firstName, and the second item of the array is assigned to a variable we called setFirstName. Note that those names are completely arbitrary and not part of the React library. Choose whatever variables feel right for your code.

The array destructuring syntax used for useState is the same as the following code:

let firstNameVariables = useState("Damien"); // This is an array with two values
let firstName = firstNameVariables[0]; // First item
let setFirstName = firstNameVariables[1]; // Second item

This way of accessing the values is a bit verbose and confusing. Therefore, the array destructuring feature is a nice way to write the useState hook.

Read state

In a class component, you would read from this.state.debateName:

<p>The debate name is {this.state.debateName}.</p>

With the state hook, we can now simply use debateName:

<p>The debate name is {debateName}.</p>

Remember: This variable name is the one you give as the first item the useState function returns.

Update state

To update state in a class component, you would use setState:

<input
  value={debateName}
  onChange={e => this.setState({ debateName: e.target.value })}
/>

With a state hook, you will use the function provided by useState, the second item it returns:

<input value={debateName} onChange={e => setDebateName(e.target.value)} />

In my example, I've called this function setDebateName. Remember that this name is not part of the React API. I've chosen the name of this function, so make them as clear as possible. Notice that we also don't need this, because we already declared debateName and setDebateName.

Recap

Let's recap how we can use a state hook:

import React, { useState } from "react";

const DebateNameInput = () => {
  const [debateName, setDebateName] = useState("");

  return (
    <div>
      <input value={debateName} onChange={e => setDebateName(e.target.value)} />
      <p>The debate name is {debateName}.</p>
    </div>
  );
};
  • On the first line, we make sure to import the useState function from React.
  • We create a function, using the arrow syntax, and give it the name DebateNameInput.
  • We call the useState function. It returns an array with two values, the current state and the function that acts as a setter. Thanks to the array destructuring syntax, we can assign those values in one line. We call the first one debateName and the second setDebateName. The useState function takes one parameter, which represents the initial state. In our example, an empty string.
  • Our function returns some JSX. The input takes the current state as its value. We gave it the name debateName. Whenever that input registers a change event, we call setDebateName with the input's new value as a parameter. This function will then replace the current debateName value with the new one.
  • React re-renders the component with that new state value.

Using several state variables

So far, we've only worked with one state variable at a time. Of course, you will most likely have more than one state variable in your component. You can use several useState functions if you want:

function fruitBasket = () => {
  const [numberOfBananas, setNumberOfBananas] = useState(0)
  const [numberOfApples, setNumberOfApples] = useState(3)
  const [numberOfPeaches, setNumberOfPeaches] = useState(2)

  //... rest of your code
}

Here, we use three different useState functions to declare three state variables, and their 3 different setters.

You don't have to use several state variables. useState can also hold objects and arrays, so this is entirely possible:

function fruitBasket = () => {
  const [numberOfFruits, setNumberOfFruits] = useState({bananas: 0, apples: 3, peaches: 2})

  //... rest of your code
}

One thing to know: updating the state variable numberOfFruits is different from the this.setState in classes. In the state hook, the setter function replaces while the setState function merges. Which means, to properly updates numberOfFruits, you'll need to:

setNumberOfFruits({ ...numberOfFruits, bananas: 2 });
setNumberOfFruits({ ...numberOfFruits, apples: 3 });

By using the spread operator (...), we keep the current state and only replaces the variables that needs to change. The first function call will replace the amount of bananas to 2, the second will replace the amount of apples to 3.

Discussion

pic
Editor guide
Collapse
sabbin profile image
Sabin Pandelovitch

A quick note only,

In your scenario

function fruitBasket = () => {
  const [numberOfFruits, setNumberOfFruits] = useState({bananas: 0, apples: 3, peaches: 2})

  //... rest of your code
}

If you have an object inside the state, and not a plain value, and you use different handlers for updating the values, you may encounter that the following

setNumberOfFruits({ ...numberOfFruits, bananas: 2 });
setNumberOfFruits({ ...numberOfFruits, apples: 2 });

if it's called parallel with other update functions may not always work correctly because your state object can be one behind in the moment of the function call.

In order to be sure that you update always has the last values you can use the hook with a callback

setNumberOfFruits(prevState => ({ ...prevState, bananas: 2 }));

I've encountered this a few times to know better

Collapse
damcosset profile image
Damien Cosset Author

That is a very good point that I forgot to mention. Thank you very much.

This is also why I always recommend to group the values that will change at the same time together, if you want to use objects in useState. If a state variable inside an object is the only one to change in an object, while the other stays the same, I prefer to put that variable in another useState call.

Collapse
Sloan, the sloth mascot
Comment deleted
Collapse
damcosset profile image
Damien Cosset Author

Indeed, I talk about the disappearance of variables in the context of React components and re-rendering to explain what state is. I understand it could be difficult to understand, and I should have make it clearer in my article.

As for the fact that something new is yet added to the library, I can understand your frustration. I shared that feeling when I felt like I was finally starting to get React pre-hooks, something new was coming up... I didn't want to learn hooks until a few months ago, even after updating my React versions. I appreciate the fact that the React team added hooks without any breaking changes for my pre-hook code.

I felt like I had to learn hooks because I was working with React so much. I do enjoy the way my code looks with Hooks, compared with classes :)

Collapse
crashley1992 profile image
crashley1992

I don't know if it's just because I'm newer to React, but I feel like it's easier to keep track of state manipulation using class components than with hooks.

This helped understand hooks a lot more.

Collapse
damcosset profile image
Damien Cosset Author

Glad it helped!

Good luck if you wish to keep going on the transition. I promise it gets better with time :D

Collapse
crashley1992 profile image
crashley1992

Thanks! I'm sure it will with more practice.

Collapse
vaibhavkhulbe profile image
Vaibhav Khulbe

Wonderfully explained! The useState() hook saves a ton of work related to internal states of components.