Cuando iniciamos un desarrollo en React muchas veces nos preguntamos como es que se puede editar una variable y que se vea reflejaeda en la pantalla. La solución por defecto que se nos propone es utilizar el Hook useState
que toma un valor base y por medio de una función actualiza el valor que necesitamos:
import React from "react";
const Component = () => {
const [count, setCount] = React.useState(0); // el valor por defecto de count será 0
return (
<div className="container">
<div>Contador : {count}</div>
<button onClick={() => setCount(count + 1)}>Incrementar</button>
</div>
);
};
export default Component;
Sin embargo, useSate
vuelve a renderizar el componente:
import React from "react";
const Component = () => {
console.log('Render component')
const [count, setCount] = React.useState(0);
return (
<div className="container">
<div>Contador : {count}</div>
<button onClick={() => setCount(count + 1)}>Incrementar</button>
</div>
);
};
export default Component;
Esto supone un problema dado que existen ocaciones donde solo queremos actualizar el valor y no perder la referencia de un render pasado.
const OtherComponent = ({ sec }: { sec: number }) => {
console.log("Render Other component - ",sec);
return (
<div className="other-component">
<h1>Other Component</h1>
<p>Seconds: {sec}</p>
</div>
);
};
const Component = () => {
const [count, setCount] = React.useState(0);
const [sec, setSec] = React.useState(0);
setInterval(() => {
setSec(sec + 1);
}, 1000);
console.log("Render component");
return (
<div className="container">
<OtherComponent sec={sec} />
<div>Contador : {count}</div>
<button onClick={() => setCount(count + 1)}>Incrementar</button>
</div>
);
};
export default Component;
Cuando se vuelve a renderizar un componente sus animaciones vuelven a un estado inicial y los cambios locales de ese componente se reinician por lo que a la larga es un problema complejo de manejar.
Pero, ¿Cómo lo podemos solucionar?
React nos provee un Hook: useRef
que mantiene en un objecto el valor actual. useRef()
devuelve un objeto { current: undefined }
y useRef(1)
devuelve un objeto { current: 1 }
por lo que podemos aprovecharnos para almacenar datos que no nos intersa que se muestren hasta que se actualice un componente:
import React, { useEffect } from "react";
import "./index.scss";
const OtherComponent = ({ sec }: { sec: any }) => {
useEffect(() => {
console.log("Other component - ",sec);
}, [sec]);
return (
<div className="other-component">
<h1>Other Component</h1>
<p>Seconds: {sec}</p>
</div>
);
};
const Component = () => {
const [count, setCount] = React.useState(0);
const sec = React.useRef(0);
useEffect(() => {
setInterval(() => {
sec.current++;
console.log('sec', sec.current);
}, 1000);
}, []);
console.log("Render component");
return (
<div className="container">
<OtherComponent sec={sec.current} />
<div>Contador : {count}</div>
<button onClick={() => setCount(count + 1)}>Incrementar</button>
</div>
);
};
export default Component;
useRef
es una buena herramienta para almacenar datos que perduran incluso despues de un re-render sin embargo los cambios que se hacen en el objecto current
no fuerza un render por lo que es necesario que un useEffect
o useState
realice una actualización.
¿ Cuando utilizamos useState
?
- Cuando queremos actualizar una variable y que su valor se muestre de inmediato
- Cuando no tenemos componentes hijos que tienen sus estados iniciales por defecto (ya que un re-render puede provocar un error)
- Cuando buscamos que nuestros componentes se actualicen con los valores actuales.
¿ Cuando utilizamos useRef
?
- Cuando queremos almacenar de forma pasiva valores para luego mostrarlos
- Cuando un componente puede tardar mucho en su renderizado
- Cuando necesitamos guardar un valor anterior y perdurarlo
En resumen:
Ambos conservan sus datos durante los ciclos de renderizado y las actualizaciones de la interfaz de usuario, pero solo el useStateHook con su función de actualización hace que se vuelvan a renderizar.