Things you need to know about React state
Tags: react, javascript, tutorial, beginners
Published: 2022-02-23 12:23:43

You probably know what is a **React state** and the difference with props. But do you know everything about it?

In this article, we will see how to use state from **`Class component`** to **`Functional component`**, things to take care, tips...

## What is it?

You probably want to add interactivity on your page and more particularily on your React component. This interaction will maybe change the UI, in this case store data into a **React state** and change the rendering in function of it is the way to go.

Unlike **`props`**, a component can change its state. But there are some rules to follow to have a re-rendering of your component when changing state. Let's see it.

## Class component

Before going into what you know nowadays i.e. **hooks**. It was a time where **hooks** did not exist and the only way to have a stateful component was to use **Component class**.

> **Note:** It's in the version `16.8.6` of `React` that hooks has been exposed by the library.

The way to make a `Component class` was to create a `class` and extends the `React.Component` class, then you have access to life-cycle methods:
- `constructor`
- `componentDidMount`
- `componentDidUpdate`
- `render` (required)
- `componentWillUnmount`

```jsx
import React from "react";

class MyClassComponent extends React.Component {
  render() {
    return <p>A simple class component</p>;
  }
}
```

----

### Initialization

Then you can initialize its state in two different ways:
- in `constructor`

```jsx
class MyClassComponent extends React.Component {
  constructor() {
    this.state = {
      firstName: "Bob",
      lastName: "TheSponge",
    };
  }
  render() {
    return <p>A simple class component with a state</p>;
  }
}
```

- declaring the property `state` directly

```jsx
class MyClassComponent extends React.Component {
  state = {
    firstName: "Bob",
    lastName: "TheSponge",
  };

  render() {
    return <p>A simple class component with a state</p>;
  }
}
```

> **Note:** You can use both as you wish. It's the same. But constructor will give you the possibility to use `props` to initialize the state.

---- 

### Access the state

As you can probably imagine you can now access the state by simply using `this.state`:

```jsx
class MyClassComponent extends React.Component {
  state = {
    firstName: "Bob",
    lastName: "TheSponge",
  };

  render() {
    return (
      <div>
        <p>First name: {this.state.firstName}</p>
        <p>Last name: {this.state.lastName}</p>
      </div>
    );
  }
}
```

----

### State update

If you have a `state` that you never update, it's probably you don't need a state to store this data.
To update, the state you have access to a method `setState` from the component instance `this`.
You can then change anything in the state. 

**Things to know about `setState`**

Unlike in component class with `useState`, `setState` will merge the updated data with the prev one automatically:

```jsx
class MyClassComponent extends React.Component {
  state = {
    firstName: "Bob",
    lastName: "TheSponge",
  };

  updateFirstName = () => {
    // It will result having a state with
    // { firstName: 'New firstName', lastName: 'TheSponge' }
    this.setState({ firstName: "New firstName" });
  };

  render() {
    const { firstName, lastName } = this.state;

    return (
      <div>
        <p>First name: {firstName}</p>
        <p>Last name: {lastName}</p>
        <button
          type="button"
          onClick={this.updateFirstName}
        >
          Update firstName
        </button>
      </div>
    );
  }
}
```

> **Warning:** It will not deeply merge. Just shallow stuff. If you need to update a single key of an object store in a state, you will have to update it in function of the previous object stored.

----

### Update state in function of the previous one

Like said in the warning above, when you want to :
- update a part of an object stored in a state
- just update the state in function of the previous one (for example for a counter)

Then you will use another API of the `setState` function.
Yep `setState` can be used with two different ways:
- passing the new state
- passing a callback with as parameter the previous state and return the new one

```jsx
class MyClassComponent extends React.Component {
  state = {
    counter: 0,
  };

  incrementCounter = () => {
    this.setState((prevState) => ({
      counter: prevState.counter + 1,
    }));
  };

  render() {
    return (
      <button type="button" onClick={this.incrementCounter}>
        Increment: {this.state.counter}
      </button>
    );
  }
}
```

> **Note:** In this case state will be "shallowly" merged too.

You may tell yourself: _It's overkill to do like that, because I have access to the previous `counter` with `this.state.counter`_

Yep you are right. But when you :
- update same property of the state multiple times in a row:

```jsx
class MyClassComponent extends React.Component {
  state = {
    counter: 0,
  };

  // This will only increment by 1 because when calling the
  // the value of `this.state.counter` is 0
  // for all 3 `setState`
  incrementByThreeCounter = () => {
    this.setState({
      counter: this.state.counter + 1,
    });
    this.setState({
      counter: this.state.counter + 1,
    });
    this.setState({
      counter: this.state.counter + 1,
    });
  };

  render() {
    return (
      <button
        type="button"
        onClick={this.incrementByThreeCounter}
      >
        Increment: {this.state.counter}
      </button>
    );
  }
}
```

- work with asynchronous stuff

```jsx
class FoodOrdering extends React.Component {
  state = {
    orderInProgressCount: 0,
    orderDeliveredCount: 0,
  };

  order = async () => {
    // I tell myself that I can destructure
    // `loading` from the state because it used at multiple place
    // but it's a bad idea
    const { orderInProgressCount, orderDeliveredCount } =
      this.state;
    this.setState({
      orderInProgressCount: orderInProgressCount + 1,
    });
    await fakeAPI();
    // In this case `loading` is still false
    this.setState({
      orderInProgressCount: orderInProgressCount - 1,
      orderDeliveredCount: orderDeliveredCount + 1,
    });
  };

  render() {
    const { orderInProgressCount, orderDeliveredCount } =
      this.state;

    return (
      <div>
        <p>Order in progress: {orderInProgressCount}</p>
        <p>Order delivered: {orderDeliveredCount}</p>
        <button type="button" onClick={this.order}>
          Order food
        </button>
      </div>
    );
  }
}
```

Play with it here:

{% codesandbox prevstate-async-cc-25d00v %}

So I recommend you the **callback API** when you need the previous value, not to have some suprise.

We have played enough with **Component classes**, now let's see how to use a state in a **Functional components**.

----

## Functional component

From the version `16.8.6`, it is possible to do stateful **Functional component** thanks to the `useState` hooks. Let check together how to use it.

### Initialization

The initial value of the state is given as parameter to the `useState` hook. There are 2 ways to do it:

- giving the value directly

```jsx
import { useState } from "react";

function StateFunctionalComponent() {
  // The initial value is 0
  useState(0);

  return <p>Functional component with state</p>;
}
```

- giving a callback to do a lazy initialization

```jsx
import { useState } from "react";

function initializeState() {
  return 0;
}

function StateFunctionalComponent() {
  // The initial value will be
  // initialized in a lazy way to 0
  useState(initializeState);

  return <p>Functional component with state</p>;
}
```

_What is the difference between the following initialization for you?_

```javascript
useState(initializeState());
```

And

```javascript
useState(initializeState);
```

Not obvious, right?

In fact in the first code the `initializeState` will be called at every render unlike the second one that will be called **only** at the **first render**.

It can be interesting to use lazy initialization when you have a process with high performance. 

### How to access the state

To know how to access we have to see what's the `useState` returns.
It will return an array, with the **value** as first element and the **updater** as second element:

```javascript
const [value, setValue] = useState('Initial value');
```

So then I just have to use the `value`.

> **Note:** You can name the `value` and the `updater` with the name you want, because there is a destructuring of the array :)

```javascript
const [counter, setCounter] = useState(0);
```

### Update the state

Then, to update the state, you just have to use the `updater`. Like with **Component class* there is 2 ways to do it:

- passing a value directly

```jsx
function Counter() {
  const [counter, setCounter] = useState(0);

  return (
    <button type="button" onClick={() => setCounter(100)}>
      Change counter: {counter}
    </button>
  );
}
```

- passing a callback which will give you access to the previous value of the state:

```jsx
function Counter() {
  const [counter, setCounter] = useState(0);

  return (
    <button
      type="button"
      onClick={() => setCounter((prev) => prev + 1)}
    >
      Increment counter: {counter}
    </button>
  );
}
```

For the same reason that I described in the [Component class part](https://dev.to/romaintrotard/things-you-need-to-know-about-react-state-43o3-temp-slug-985098?preview=2f2cd6eead9546d18f5990c257f2f3202dd42f8c1a452ba09725d7e80757ad2d1666f90b32a132fa01031921af6a4ee2126420afbda87d22e1ac6b7b#update-state-in-function-of-the-previous-one), I recommend to use the callback API when you need the previous value.

----

### Things to know about state in Functional Component

#### No merge done automatically
When you update a state in a Function component, there is no merge of the state. So if your state has an object it will remove all key that you do not pass during the update:


```jsx
function Person() {
  const [person, setPerson] = useState({
    firstName: "Bob",
    lastName: "TheSponge",
  });

  const updateFirstName = () => {
    // When doing that you will lose the lastName key
    // in your person object
    setPerson({ firstName: "Romain" });
  };

  return (
    <div>
      <p>First name: {firstName}</p>
      <p>Last name: {lastName}</p>
      <button type="button" onClick={updateFirstName}>
        Update firstName
      </button>
    </div>
  );
}
```

#### Store a function a in a state

Because, the APIs of `useState` can take a callback during initialization and when updating the state. If you want to store a function, you will have to use the callback API during both, otherwise your function **will be executed** and the **returned value** will be stored:

```jsx
function firstFunction() {
  // Do some stuff
  return "Hello";
}

function secondFunction() {
  // Do some stuff
  return "Guys and girls";
}

export default function MyComponent() {
  // If you do `useState(firstFunction)`
  // It will be 'Hello' that will be stored
  const [myFunction, setMyFunction] = useState(
    () => firstFunction
  );

  const changeFunction = () => {
    // If you do `setMyFunction(secondFunction)`
    // It will be 'Guys and girls' that will be stored
    setMyFunction(() => secondFunction);
  };

  return (
    <button type="button" onClick={changeFunction}>
      Change the function stored: {myFunction.toString()}
    </button>
  );
}
```

#### Working with asynchronous code

In most case, React will batch your state updates to result in a single render. For example in `useEffect` / `useLayoutEffect` and in event handlers.

For example, when clicking on the button on the following code, will result in a single render with the new `firstName` and `lastName`:

```jsx
function MyComponent() {
  const [firstName, setFirstName] = useState("Bob");
  const [lastName, setLastName] = useState("TheSponge");

  return (
    <button
      type="button"
      onClick={() => {
        setFirstName("Patrick");
        setLastName("Star");
      }}
    >
      Change name
    </button>
  );
}
```

But when you work with asynchronous code, for example if you fetch the new name with a **REST API**, it will result in multiple render:

```jsx
function fakeAPI() {
  return new Promise((resolve) =>
    setTimeout(
      () =>
        resolve({ firstName: "Patrick", lastName: "Star" }),
      500
    )
  );
}

function MyComponent() {
  const [firstName, setFirstName] = useState("Bob");
  const [lastName, setLastName] = useState("TheSponge");

  return (
    <button
      type="button"
      onClick={async () => {
        const newName = await fakeAPI();

        // It will result into 2 render
        // firstName: 'Patrick' and lastName: 'TheSponge'
        // firstName: 'Patrick' and lastName: 'Star'
        setFirstName(newName.firstName);
        setLastName(newName.lastName);
      }}
    >
      Change name
    </button>
  );
}
```

In this case, we will prefer to do a single state which will have both `firstName` and `lastName` values because these values are tied together. But it can happen that updated values have no relationship but we sometimes need to update them together, in this case we will do separate state, and will have to make attention of the order of state updates.

> **Note:** With **React v18** these cases will not happen anymore, and updates will be also batched.

> **Note:** In a next article, we will see how the batching of state works under the hood and how we can force the batching of updates in an application.

----

## What not to do with states

This rule is valid for both Component class and Functional component. **Do not mutate** a state.
For example, don't do that:

```jsx
function Person() {
  const [person, setPerson] = useState({
    firstName: "Bob",
    lastName: "TheSponge",
  });

  return (
    <div>
      <p>First name: {firstName}</p>
      <p>Last name: {lastName}</p>
      <button
        type="button"
        onClick={() =>
          setPerson(
            (prevState) => (prevState.firstName = "Romain")
          )
        }
      >
        Update firstName
      </button>
    </div>
  );
}
```

_Why won't it work?_

When you call the update callback, React will compare with strict equality the previous state to the new one, if it's the same then React will not trigger a re-render.

![Condition to trigger render from state updates](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0roegcad31bgp86iqypj.png)

> **Note:** And do `person.firstName = 'Romain'` will do nothing at all.

----

## Conclusion

Using **React state** is not a hard thing and is really important to know how to work with it properly:
- do not mutate the state
- when you need the previous value of the state, prefer to use the version with callback

If you want to lazily initialize your state in Functional component, because of performance cost for example, think to use the callback initialization.

One last point, if the state is not used for the UI maybe the use of a `state` is not the right choice, a `ref` (`useRef`) would probably be a better option. It's something we will see in a next article :)

----

Do not hesitate to comment and if you want to see more, you can follow me on [Twitter](https://twitter.com/romain_trotard ) or go to my [Website](https://www.romaintrotard.com).