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;
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;
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.
👀 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:
con spread esto sería una copia:
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 de referencia
¡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!
