React Hooks: Managing State With useState Hook
Tags: react, javascript, codenewbie
Published: 2020-10-26 20:26:02

## Hello World 👋

Hooks are special types of functions in React that you can call inside React functional components. They let you store data, add interactivity, and perform some actions, otherwise known as side-effects.

Below are the most common hooks that you use:
- useState
- useEffect
- useRef
- useContext
- useReducer

In this article, we will learn in-depth about `useState` hook.

## useState
`useState` is a built-in function in React. It takes a single argument and returns an array of two elements when executed.

Let's see an example.
```js
const [count, setCount] = useState(0)
```

- It is used to manage the state of the component.
- When the state of a component changes, React re-renders that component and all of its child components automatically.
- It takes the initial value of the state as an argument.
- It returns an array of two elements.
    - The first element is the value of the state.
    - The second element is a function that you can use to set the value of the state
    - You can name these elements whatever you like, but the common practice is to name them as `var` and `setVar`. For instance, in the above example, we named it as `count` and `setCount`.

In the above example, we called `useState` with the argument of 0. This means that the initial value of the state is 0. `count` contains the value of the state. `setCount` is a function you can use to set the value of count.

Let's see the complete example of a component to get an idea of how `useState` is used in React.

```js
import React, { useState } from "react"

function Counter() {
    const [count, setCount] = useState(0)
    function increment() {
        setCount(count + 1)
    }
    return (
        <button onClick={increment}>{count}</button>
    )
}
```

This renders a simple button that shows the value of count. Initially, it is 0. Whenever you click on the button, the count value is increased by 1 by using `setCount`. And as soon as the state changes, the component rerenders and the new value of count is shown in the button.

{% codesandbox simple-counter-k5io2 runonclick=1 %}

### Functional Updates

Let's slightly change the above component.
```js
import React, { useState } from "react"

function Counter() {
    const [count, setCount] = useState(0)
    function incrementBy3() {
        setCount(count + 2)
        setCount(count + 1)
    }
    return (
        <button onClick={incrementBy3}>{count}</button>
    )
}
```

Now, when you click on the button, what would you expect the increment to be. Will the count be incremented by 2? (or) Will it be incremented by 1? (or) Will it be incremented by 3?

Click and try it out.

Here is the corresponding code sandbox for it.

{% codesandbox simple-counter-2-ff4pc runonclick=1 %}

You can observe that the count will only be incremented by 1. This is because of how React makes state updates. Many state updates are batched together and performed in an asynchronous manner. So, if you have two setState calls at the same place, you cannot rely on React to complete the first state update before doing the second state update.

Let's take the above example. 
- Initially, the `count` is 0.
- Button is clicked.
- First, React starts executing `setCount(count + 2)`. 
    - The value of `count` in that render is `0`. 
    - React calculates the value of `count` for the next render to be `count + 2`, which is 2. 
    - But the component is not yet re-rendered. So the current value of the variable count is still `0`.
- Now, React starts executing `setCount(count + 1)`. 
    - Since the value of `count` is still 0, React calculates the value of `count` for the next render to be `count + 1` which is 1. 
    - The value of next state was 2 when `setCount(count+2)` is executed. Now, it got overridden by the value of next state of `setCount(count+1)` which is 1.
- Now since all the state updates are executed, React starts rerendering the component with the value of the next state which is 1.
- And this is the reason why, when you click on the button only 1 is getting incremented instead of 3.

Even though it takes a while to understand the problem that is occurring, the solution to fix this problem is not that complicated.

Till now we have seen that `setCount` takes a value as an argument. But it also takes a callback as the argument of `setCount`. The first argument of that callback function is the previous value of the state.

For example, if we want to increment the count by 1, you can do it as follows:
```js
setCount(previousCount => previousCount + 1)
```

When you use this type of callback function to update the state, you can be assured that the `prevCount` will always have the correct value of the state even though the component is not yet rerendered. That is why it is always recommended to use this type of update whenever the next value of the state is computed from the previous value of the state.

Let's use this approach and rewrite the above example.
```js
import React, { useState } from "react"

function Counter() {
    const [count, setCount] = useState(0)
    function incrementBy3() {
        setCount(previousCount => previousCount + 2)
        setCount(previousCount => previousCount + 1)
    }
    return (
        <button onClick={incrementBy3}>{count}</button>
    )
}
```
This correctly increments the count by 3.

{% codesandbox simple-counter-3-9cxe0 runonclick=1 %}

### Lazy Initialization
```js
const initialValue = resultOfSomeExpensiveOperation()
const [state, setState] = useState(initialValue)
```

Previously we have seen that the `useState` takes the initial value as an argument. 

Technically React only needs the initialValue when the component is first mounted. After that, the initialValue is no longer applicable. So if the initial value is calculated through some expensive operations, we want those operations to run only at the start. Let's see if that is actually happening or not through an example.

```js
import React, { useState } from "react";

function getInitialValue() {
  console.log('getInitialValue is getting executed');
  // ... do some expensive operations
  return 0;
}

function Counter() {
  const [count, setCount] = useState(getInitialValue());
  function increment() {
    setCount(count + 1);
  }
  return <button onClick={increment}>{count}</button>;
}
```

{% codesandbox simple-counter-4-8w5u2 runonclick=1 %}

Try to click on the button and check the console. 
- You will see that the `getInitialValue` function is being executed every time the button is clicked. So, it is getting called on every rerender.
- So, if there are any expensive operations to be made in that function, they will be executed after every rerender.
- But only the first execution of the `getInitialValue` is useful for React. All others will be thrown away as the state is already set in the subsequent rerenders.
- You can see why this might cause performance problems for your application.

React gives us a way to handle this type of situation. It's called `Lazy Initialization of State`.

Instead of directly passing the value as an argument, you have the option to pass a callback function which when executed gives you the initial value. React executes this function only when it is needed. This is needed only at the first, so React executes this function only once at the start.

Let's rewrite the above example to use lazy state initialization.

```js
import React, { useState } from "react";

function getInitialValue() {
  console.log('getInitialValue is getting executed');
  // ... do some expensive operations
  return 0;
}

function Counter() {
  const [count, setCount] = useState(() => getInitialValue());
  function increment() {
    setCount(count + 1);
  }
  return <button onClick={increment}>{count}</button>;
}
```

All we changed in the above example is that: `useState(getInitialValue())` is changed to `useState(() => getInitialValue())`.

{% codesandbox simple-counter-5-2rois runonclick=1 %}

You can check the console of the above code sandbox. You will see that `getInitialValue` is not getting called when you click on the button. It is only called at the start. 

### useState with Objects
You can manage any type of state with `useState` hook, even objects.

For example, let's use useState hook to manage firstName and lastName states in a single object.
```js
const [name, setName] = useState({
    firstName: 'Bhanu Teja',
    lastName: 'P'
})
```

Now, whenever you call `setName` to update the name object, you have to provide both firstName and lastName.

For example,
```js
setName({
    firstName: 'New First Name',
    lastName: 'New Last Name'
})
```

What if we want to update only the firstName or only the lastName. You can do so by using the spread operator.

```js
function setFirstName(firstName) {
    setName({
        ...name,
        firstName
    })
}

function setLastName(lastName) {
    setName({
        ...name,
        lastName
    })
}
```

Let's put everything together.

```js
import React, { useState } from "react";

function App() {
  const [name, setName] = useState({
    firstName: "Bhanu Teja",
    lastName: "P"
  });

  function setFirstName(firstName) {
    setName({
      ...name,
      firstName
    });
  }

  function setLastName(lastName) {
    setName({
      ...name,
      lastName
    });
  }

  return (
    <>
      <input
        placeholder="First Name"
        type="text"
        value={name.firstName}
        onChange={(e) => setFirstName(e.target.value)}
      />
      <input
        placeholder="Last Name"
        type="text"
        value={name.lastName}
        onChange={(e) => setLastName(e.target.value)}
      />
      <p>
        Your name is:{" "}
        <strong>
          {name.firstName}.{name.lastName}
        </strong>
      </p>
    </>
  );
}
```

{% codesandbox usestate-with-objects-2r78p runonclick=1 %}

Usage of `useState` with arrays is very similar to that of with objects.

## What have you learned? 
You learned about:
- What are hooks and what are some of the common hooks in React ?
- `useState` hook:
    - It takes the initial value of the state as an argument and returns an array of two elements - one has the value of the state and the other is a function to update the value of the state.
    - How to do functional updates for the state? 
    - Why, not using functional updates, causes a problem in certain situations ?
    - It is always recommended to use functional updates to update the state when the next state is computed from the previous state.
    - Lazy initialization of state and when it can be useful.
    - Usage of `useState` with objects and arrays.


## What's Next?
In the next article, we will learn everything about `useEffect` hook.

#### Until Next Time 👋
---

If this was helpful to you, Please **Like** and **Share** so that it reaches others as well. To get email notifications on my latest articles, please subscribe to [my blog](https://blog.bhanuteja.dev) by hitting the **Subscribe** button at the top of the page. You can also follow me on Twitter [@pbteja1998](https://twitter.com/pbteja1998).

[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/J3J72JK30)