React hooks: useState
Published: 2022-02-02 10:50:38

## Índice:
* [Descripción](#description)
* [Diferencias con this.setState](#differences)
* [Lazy initial state](#initial-state)
* [Functional updates](#functional-updates)

## Descripción<a name="description"></a>
**useState** nos permite agregarle un estado a un componente funcional.

Retorna un par de valores:
- el estado.
- una función para poder actualizarlo.

```react
  const [count, setCount] = useState(0);
```

En nuestro ejemplo anterior, `count` es nuestro estado y está inicializado con el valor `0` y `setCount` es nuestra función para poder actualizarlo.

> **Dato**:
> _Observemos que estamos haciendo una [destructuración](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) de un array cuando ejecutamos el hook `useState`, lo que nos da la posibilidad de poder llamar a nuestro estado y función para actualizarlo como querramos. Por convención la función debe comenzar con `set`_.

`useState` espera un solo argumento, el cual sería el valor por defecto que tendría nuestro estado. A diferencia del estado en los componentes de clases, nuestro estado pueden ser strings, numbers, etc.

```react
  import { useState } from 'react';

  const [count, setCount] = useState(0);
  const [text, setText] = useState('Hi there!');
  const [user, setUser] = useState({
    name: 'Denis',
    job: 'Frontend Enginner'
  });
  const [shoppingList, setShoppingList] = useState([
    {
      item: 'laptop',
      price: 900,
      currency: '€'
    }
  ]);
```

### [Volver al índice](#start)

---

## Diferencia con `this.setState`<a name="differences"></a>

Tenemos que tener en cuenta a la hora de actualizar nuestro estado que a diferencia del `this.setState` de los componentes de clase donde el estado es siempre un objeto y podemos actualizar una propiedad del mismo directamente sin afectar al resto, que nuestra función devuelta por el hook `useState` va a reemplazar por completo nuestro estado por el valor que nosotros le pasemos, por ende, si nuestro estado por ej, es un objeto y nosotros quisiéramos actualizar una sola propiedad, deberíamos pasarle la propiedad actualizada y además, el resto de sus propiedades.

### Class component
```react
  // ...
  constructor(props) {
    super(props);
    // Definimos nuestro estado
    this.state = {
      count: 0,
      text: "class text"
    };
    this.onClick = this.onClick.bind(this);
  }

  onClick() {
    // Destructurando obtenemos el valor de count
    const { count } = this.state;
    // Actualizamos el valor de count sin afectar
    // el valor de text
    this.setState({ count: count + 1 });
  }
  // ...
```

### Functional component
```react
  // ...
  // Destructurando obtenemos los valores de count y text
  const [{ count, text }, setState] = React.useState({
    count: 0,
    text: "functional text"
  });

  const onClick = () => {
    // Actualizamos el valor de count y mantenemos el de text
    setState({
      count: count + 1,
      text
    });

    // También podríamos actualizar nuestro estado haciendo
    // uso de una función. (Recomendado)
    setState(prevState => ({
      ...prevState,
      count: count + 1
    }));
  }
  // ...
```

> **Dato**:
> _Si a `setState` solo le pasariamos el valor de `count`, perderíamos todo rastro de `text`, dejaría de existir_.

Ejemplo en [CodeSandbox](https://codesandbox.io/s/usestate-3-bsif2)

#### [Volver al índice](#start)

---

## Lazy initial state<a name="initial-state"></a>

Si el valor por defecto que queremos asignarle a nuestro estado es el resultado de un <u>calculo costoso</u>, podemos utilizar el **lazy initial state**, es decir, es vez de pasarle nuestro estado inicial, lo que hacemos es pasarle una función que lo calcule, esto se ejecutará una sola vez en el render inicial.

```react
function getExpensiveState() {
  // Calculo costoso
}

// Nuestro componente va a realizar el calculo costoso
// en cada render aunque no utilice su valor más allá del
// primer render.
function ComponentWithoutLazy() {
  const [state, setState] = React.useState(getExpensiveState());

  // ...
}

// Ahora nuestro componente solo va a realizar el calculo
// costoso en el primer render.
function ComponentWithLazy() {
  const [state, setState] = React.useState(() => getExpensiveState());

  // ...
}
```

Ejemplo en [CodeSandbox](https://codesandbox.io/s/usestate-1-nxn2s)

### [Volver al índice](#start)

---

## Functional updates<a name="functional-updates"></a>

Cuando nuestro componente es simple y no contiene muchas actualizaciones de estado ejecutadas por diversos eventos, cambios de props, etc, no vamos a tener problemas actualizando nuestro estado seteando el nuevo directamente.

```react
function Counter() {
  const [count, setCounter] = React.useState(0);

  function onClick() {
    setCounter(count + 1);
  }

  // Code
}
```

Pero cuando nuestro componente comienza a manejar más lógica y este estado es actualizado en distintas oportunidades por distintos eventos, acciones, etc. Lo mejor y más recomendable, es utilizar una función para actualizar nuestro estado con `setState`, de esta manera:

```react
function Counter() {
  const [count, setState] = React.useState(0);

  function onClick() {
    setState(prevState => ({
      ...prevState,
      count: prevState + 1
    }));
  }

  // Code
}
```

**Por qué?** Porque utilizando una función nos aseguramos de tener **el último valor de nuestro estado** para poder actualizarlo en consecuencia y evitar inconsistencias o valores erróneos.

Ejemplo en [CodeSandbox](https://codesandbox.io/s/usestate-2-b2dih)

### [Volver al índice](#start)