useState() like a Pro ✨
Tags: react, javascript, beginners
Published: 2021-02-07 08:48:33

This blog post covers all you need to know about the concept of a state and react `useState` hook from basics to advanced patterns. This blog assumes that you know about react fundamentals like Component, props, and jsx.


>  `counter` is the `hello world` of state management.


## What is a state and why do we need state management in React? ⚛️

```js
function Counter() {
  // State: a counter value
  const [counter, setCounter] = useState(0)

  // Action: code that causes an update to the state when something happens
  const increment = () => {
    setCounter(counter + 1)
  }

  // View: the UI definition
  return (
    <div>
      Value: {counter} <button onClick={increment}>Increment</button>
    </div>
  )
}
``` 

as you can see in this example there three main parts to `Counter` component

- **State** is the truth that drives our application
- **View** is UI based on the state
- **Action** is an event that occurs in the application and changes the state

React uses the stateful values  _(which is generated by hook APIs like useState, useReducer)_  to know when to update the UI (View) part of an application. Whenever this state value changes React will update the component so that the state of the UI is the same as the state of the Component.

## useState Hook 🎣


`useState` is a function that accepts one argument as an initial value of any type for the state, and returns 2 elements in an array, first is the state value and the second is the updater function that we use to update the state value. This returned array is usually destructured so we can name the variables whatever we want but it is good practice and a common convention to prefix the `set` in front of the updater function.

```js
// you can pass any data-type
setState() // if you don't pass anything than value will be updated with undefined 
setState('Thanks') // String
setState(4) // Number
setState(['reading']) // array 
setState({ share : 💗 }) // object
setState(null) // null 
```

>In our Counter component `0` is the initial value for the state, and using destructuring we are naming our 2 variables `counter` and `setCounter`. `setCounter` is used to update the counter value by 1 every time the button is clicked.

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

  const increment = () => {
    setCounter(counter + 1)
  }

  return (
    <div>
      Value: {counter} <button onClick={increment}>Increment</button>
    </div>
  )
}
``` 

## Lazy initialization of state 🦥

Every time React re-renders the component, `useState(initialState)` is executed. if the initial state is some expensive function computation, e.g reading data from localStorage, mapping over some large amount of data, the instance with multiple methods ( e.g `DraftJs` or `ThreeJs` instance), then the component might face some performance issues.

```js
// format : useState(() => initalState) 

const [token, setToken] = useState(() => window.localStorage.getItem('token') || '')

```

we can use the __lazy initalization__ to avoid the performance bottleneck for this all you need to do is put your initial state in function and that's it.

## Update the state with callback 🤙

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

 const increment = () => {
    setCounter(counter + 1);
    setTimeout(() => {
      setCounter(counter + 1);
    }, 1000);
  };
```

we have changed the increment function of the previous example, now we have added asynchronous behavior in our function what do you think the output will be?

<br><br><br><br><br><br><br>
Take a pause and think , 
<br><br><br><br><br><br><br>
*Spoilers Ahead*
<br><br><br><br><br><br><br>

You would see that after clicking the button once, even though we have 2 `setCounter` calls, we still get a new count updated with **1 only**.

### So what is actually happening?  🤔

The problem is that the second call to the `setCounter` is getting the same counter value as the first one. here in the example, both the `setCounter` got the value of counter as `0` so they updated it to `1`.

### But why 2nd the updater is getting the value of 0? 😕

For this, you need to understand how re-rendering actually works in React, We will not into the depths of this but in short re-rendering means if your state changes your whole component is replaced with the new one, In this example whole `Counter` is called again and then it gets the new value. here we are using multiple consecutive updates and due to  [_closure_](https://whatthefork.is/closure) `setCounter` has access to the `counter` variable one we got from array destructuring which has a value of 0.

In the example we have the initial value as `0` when the button is clicked we update the value from 0 -> 1 but to get that updated state(1) react needs to re-render the component but here we are calling the `setCounter` again to update counter with +1 and it gets counter as `0` so after one second when it updates the value `1`.

>For most `async` activities `useEffect` would be a better choice. and for more complex states `useReducer`.

### Solution 🔥
When the new state is dependent on the previous state you can update the state with a callback.

```js
const increment = () => {
    setCounter(counter + 1);
    setTimeout(() => {
      // callback inside a updater function
      setCounter(counter => counter + 1);
    }, 1000);
  };
```

If you would change the increment function with this new one you would have a reference to the internal state and not the closure value of state.

### Use-cases 💼

```js
// toggle a boolean
const [toggled, setToggled] = useState(false);
setToggled(toggled => !toggled);

// Update an object 
const [size, setSize] = useState({ height : 500, width : 800})
setSize(currentSize => ({...currentSize , height : 700}))

// Update items in array
const [items, setItems] = useState([]);
setItems(items => [...items, 'push']);
```