Reaccionar renderizado innecesario del elemento
Acabo de comenzar a aprender React y estaba creando una pequeña aplicación que hace llamadas a una API de cotizaciones. La API tiene un punto final que devuelve una cotización aleatoria. Cuando la aplicación se carga inicialmente, realiza una llamada a la API y muestra una cotización, y hay un botón en el que se puede hacer clic para obtener una nueva cotización aleatoria (nueva llamada a la API).
Tengo un componente raíz llamado App. Este componente tiene un componente QuoteWrap como hijo. El componente QuoteWrap tiene dos hijos: el botón que se utiliza para obtener una nueva cita aleatoria y un componente de Cotización que muestra el autor de la cita y la cita en sí. Este es el código dentro del componente QuoteWrap:
export default function QuoteWrap() {
const { quoteData, isLoading, fireNewCall } = useQuote();
const handleClick = () => {
fireNewCall();
};
return(
<>
<button onClick={handleClick}>Get random quote</button>
{ isLoading ?
<h2>Loading...</h2>
:
<Quote author={quoteData.author} quote={quoteData.quote} />
}
</>
);
}
useQuote () es un gancho personalizado que administra las llamadas a la API y devuelve 3 valores: 1- los datos, 2- si hay una llamada en proceso y 3- una función para realizar una llamada a la API.
Obviamente, cada vez que se hace clic en el botón, todo el componente QuoteWrap se vuelve a renderizar (como quoteData e isLoading cambian). Pero, en realidad, no es necesario volver a renderizar el botón, ya que nunca cambia.
Entonces pensé: ok, puedo mover el botón hasta el componente de la aplicación. Pero entonces no tengo acceso a la función fireNewCall en el gancho useQuote.
¿Cómo puedo evitar que se vuelva a renderizar el botón? ¿Es incluso importante en este caso o me estoy obsesionando demasiado con las re-renderizaciones de React?
¡Gracias!
Respuestas
Su componente se volverá a renderizar cada vez que cambie la función handleClick, que es cada vez que se renderice QuoteWrap.
La solución es el gancho useCallback. useCallback devolverá la misma función para handleClick, cada vez que se procese QuoteWrap, siempre que las dependencias no hayan cambiado.
https://reactjs.org/docs/hooks-reference.html#usecallback
Lo usarías así:
const handleClick = useCallback(() => {
fireNewCall();
},[fireNewCall]);
fireNewCall es la dependencia, por lo que siempre que useQuote devuelva una función fireNewCall estable, entonces su botón no se volverá a renderizar, ya que la propiedad handleClick no ha cambiado.
Creo que es posible que te obsesiones demasiado con las repeticiones de render de React. El botón debe volver a renderizarse porque, en algunos casos , handleClickdebe cambiarse cuando se fireNewCallcambie. Incluso si, handleClicknunca se cambiará. No es necesario pensar en volver a renderizar un elemento.
Más o menos lo que dijeron Benjamin y Viet: en su código original, se asigna una nueva función para manejarHaga clic en cada renderizado. Puede usar React.useCallbackpara mantener la referencia de función original y solo actualizarla cuando algo en la matriz de dependencia cambie; en este caso, solo fireNewCallnecesita ir a la matriz de dependencia.
Pero como dice Viet, no se obsesione demasiado con eso. El uso React.useCallbackpodría incluso ralentizar su código. Consulte Kent C. Dodds When to useMemo y useCallback post para obtener más información.