logo blog voltadev
useState

useState

- views - 03-03-2023

Hola bienvenidos al blog 😀 este post veremos a profindidad useState

useState

Para producir un nuevo renderizado necesitamos un estado y no nos basta con un simple variable, ni con una variable local, ni modificar una prop.

Definiendo un estado

Para definir un estado, simplemente tenemos que usar dentro del componente en el que lo queremos crear, el hook useState que viene directamente de react.


  • useState recibe un valor inicial, puede ser valor fijo o puede venir directamente de una prop.

Vamos a hacer un console.log() de state

import React, { useState } from "react";

const Likes = ({ likes }) => {
  const state = useState(0);

  console.log(state);
  return <button onClick={() => {}}>{likes} likes</button>;
};

const App = () => {
  return <Likes likes={2}>App</Likes>;
};

export default App;
array usestate log

como vemos este state nos devuelve un array de 2 elementos

  • primer elemento -> el valor inicial
  • segundo -> una función actualizadora

este estado se puede desestructurar como un array


¿Qué ocurre cuando hago un setState?

Se Provoca un nuevo renderizado, damos click y podemos ver en consola que se vuelve a renderizar "Render Likes" pero no estamos renderizando la aplicación. Porque likes es el nodo del virtual DOM que tiene el estado, quiere decir en el componente donde hemos hecho el useState, entonces lo que hacemos al modificar un estado NO es renderizar absolutamente toda la aplicación, el cambio de estado no puede afectar a nada que no esté colgando de ese nodo. Entonces no es necesario volver a renderizar toda la aplicación, simplemente la parte que se ve afectada. Esa parte es la que define la función Likes.

¿Qué pasa si tenemos otro componente llamado Button?

import React, { useState } from "react";

const Likes = () => {
  console.log("Render Likes");

  const [likes, setLikes] = useState(0);

  return <Button onClick={() => setLikes(likes + 1)}>{likes} likes</Button>;
};

const Button = ({ onClick, children }) => {
  console.log("Render Button");
  return <button onClick={onClick}>{children}</button>;
};

const App = () => {
  console.log("Render App");
  return <Likes />;
};

export default App;

render app

En el primer render vamos a tener

Render App
Render Likes
Render Button

luego al darle click vamos a tener

Render App
Render Likes
Render Button
Render Likes
Render Button

Likes devuelve el componente Button que es otra función que tenemos definida.

Este estado siempre está asociado a un componente, cuando ocupamos un setState nos proporciona actualizar -> provocaremos un nuevo renderizado. Pero este nuevo renderizado no será de toda la aplicación, si no solamente de la función que define el componente que la utiliza.

Por eso es obligatorio que useState se utilice dentro de un componente, si saco este hook de dentro del componente mi App deja de funcionar.

error hook

👀 Esto quiere decir que los hooks nunca se pueden llamar desde afuera de un componente.

Renderizado 👁️

¿Cuándo se produce realmente este nuevo render? Al llamar al setSate setLikes producimos un nuevo renderizado, pero esto no siempre es así, para que se produzca un nuevo renderizado, el valor del estado tiene que cambiar respecto a lo que tenemos antes y para ello tenemos que asegurarnos que el nuevo valor que le pasemos a este setLive es distinto del anterior.

import React, { useState } from "react";

const Likes = () => {
  console.log("Render Likes");

  const [likes, setLikes] = useState(0);

  return <Button onClick={() => setLikes(7)}>{likes} likes</Button>;
};

const Button = ({ onClick, children }) => {
  console.log("Render Button");
  return <button onClick={onClick}>{children}</button>;
};

const App = () => {
  console.log("Render App");
  return <Likes />;
};

export default App;

Cambiamos el valor por 7 y tenemos esto:

Render App
Render Likes
Render Button
Render Likes
Render Button
Render Likes

al volver a darle click luego del primer renderizado se muestra:

Render Likes
Render Button

pero al volver a dar click solo se muestra una vez:

Render Likes

Solamente renderizamos like cuando hago un setState con el mismo valor que teníamos anteriormente, lo que voy a producir no es un nuevo renderizado. Este renderizado es solo de comprobación.

🛑 No hay que hacer setSate sino son necesarios, si vale 7 para qué voy a hacer un setState para volver a poner el mismo valor. Aquí estábamos comprobando primitivos, ahora pasaremos a ver referencias.

Comparando Referencias

Vamos a ver un poco de como un valor apunta a la misma referencia:

referencia

con spread esto sería una copia:

obj-spread

Esto es muy importante cuando trabajamos con estados, porque react va a ver si a es igual b.

import React, { useState } from "react";

const Likes = () => {
  console.log("Render Likes");

  const [likes, setLikes] = useState({ val: 0 });

  return (
    <Button
      onClick={() => {
        likes.val = likes.val + 1;
        setLikes(likes);
      }}
    >
      {likes.val}likes
    </Button>
  );
};

const Button = ({ onClick, children }) => {
  console.log("Render Button");
  return <button onClick={onClick}>{children}</button>;
};

const App = () => {
  console.log("Render App");
  return <Likes />;
};

export default App;

Este like es la misma referencia no va a funcionar

import React, { useState } from "react";

const Likes = () => {
  console.log("Render Likes");

  const [likes, setLikes] = useState({ val: 0 });

  return (
    <Button
      onClick={() => {
        const newLikes = { ...likes };
        newLikes.val++;
        setLikes(newLikes);
      }}
    >
      {likes.val}likes
    </Button>
  );
};

const Button = ({ onClick, children }) => {
  console.log("Render Button");
  return <button onClick={onClick}>{children}</button>;
};

const App = () => {
  console.log("Render App");
  return <Likes />;
};

export default App;

esto funciona con spread, o si no declaro un nuevo objeto

import React, { useState } from "react";

const Likes = () => {
  console.log("Render Likes");

  const [likes, setLikes] = useState({ val: 0 });

  return (
    <Button
      onClick={() => {
        const newLikes = { val: likes.val };
        newLikes.val++;
        setLikes(newLikes);
      }}
    >
      {likes.val}likes
    </Button>
  );
};

const Button = ({ onClick, children }) => {
  console.log("Render Button");
  return <button onClick={onClick}>{children}</button>;
};

const App = () => {
  console.log("Render App");
  return <Likes />;
};

export default App;

Esto es perfectamente posible

Cuando vayamos a asignarle una variable al state, la variable tiene que ser distinta, si no es distinta, no cambia ni hace absolutamente nada o declaras un nuevo objeto o usas el spread

Siempre que trabajemos con objetos debemos hacer una copia en algún momento para no apuntar a la misma referencia del initial State, pasas el spread operator, pero nunca asignes a un state el valor que ya tenía, esto también es válido.

👀Importante recordar

useState actualiza de forma asíncrona.

🔗 Link Documentación useState

¡Muchas gracias por tu lectura dejo el link de cafecito apoyo voluntario, tu contribución me motiva a seguir creando contenido de alta calidad. ¡Muchas gracias por tu apoyo!


Buy Me a Coffee at ko-fi.com