javascript - useMemo vs. useEffect + useState
¿Hay algún beneficio en usar useMemo (por ejemplo, para una llamada a función intensiva) en lugar de usar una combinación de useEffect y useState?
Aquí hay dos hooks personalizados que funcionan exactamente igual a primera vista, además de que el valor de retorno de useMemo es null en la primera renderización:
useEffect y useState
import { expensiveCalculation } from "foo";
function useCalculate(someNumber: number): number | null {
const [result, setResult] = useState<number | null>(null);
useEffect(() => {
setResult(expensiveCalculation(someNumber));
}, [someNumber]);
return result;
}
useMemo
import { expensiveCalculation } from "foo";
function useCalculateWithMemo(someNumber: number): number {
return useMemo(() => {
return expensiveCalculation(someNumber);
}, [someNumber]);
};
Ambos calculan el resultado cada vez que su parámetro someNumber cambia, ¿dónde entra en juego la memorización de useMemo?
Respuesta #1
El useEffect y el setState causarán renderizados adicionales en cada cambio: el primer renderizado estará "retrasado" con datos obsoletos y luego inmediatamente encolará un renderizado adicional con los datos nuevos.
Supongamos que tenemos:
// Maybe I'm running this on a literal potato
function expensiveCalculation(x) { return x + 1; };
Supongamos que x es inicialmente 0:
- La versión
useMemorenderiza inmediatamente1. - La versión
useEffectrenderizanull, luego de que el componente se renderiza, el efecto se ejecuta, cambia el estado y encola un nuevo renderizado con1.
Entonces, si cambiamos x a 2:
- El
useMemose ejecuta y se renderiza3. - La versión de
useEffectse ejecuta y vuelve a renderizar1, luego el efecto se activa y el componente se vuelve a ejecutar con el valor correcto de3.
En términos de la frecuencia con la que se ejecuta expensiveCalculation, ambos tienen un comportamiento idéntico, pero la versión con useEffect está causando el doble de renderizado, lo cual es malo para el rendimiento por otras razones.
Además, la versión useMemo es simplemente más limpia y legible, en mi opinión. No introduce un estado mutable innecesario y tiene menos elementos móviles.
Así que te conviene más simplemente usar useMemo aquí.
Respuesta #2
Creo que hay dos puntos principales que deberías considerar al elegir entre ellos.
- Hora en que se llamó la función.
Se llama a useEffect después de que el componente ha sido renderizado, por lo que puedes acceder al DOM desde él. Por ejemplo, esto es importante si deseas acceder a elementos del DOM a través de refs.
- Garantías semánticas.
useEffect garantiza que no se ejecutará si las dependencias no han cambiado. useMemo no ofrece tales garantías.
Como se indica en la documentación de React, deberías considerar useMemo como una técnica de optimización pura. Tu programa debería seguir funcionando correctamente incluso si reemplazas useMemo con una llamada de función regular.
useEffect + useState se pueden usar para controlar actualizaciones. Incluso para romper dependencias circulares y prevenir bucles de actualización infinitos.
Respuesta #3
Diría que aparte de la naturaleza asincrónica, podría haber alguna diferencia en términos de cómo están diseñados.
useEffect es una llamada colectiva, asíncrona o no, se recoge después de que todos los componentes se hayan renderizado.
useMemo es una llamada local, que solo tiene que ver con este componente. Podrías simplemente pensar en useMemo como otra instrucción de asignación con beneficios de usar la asignación de la última actualización.
Esto significa que useMemo es más urgente, luego useLayoutEffect y el último es useEffect.