Cómo comenzar rápidamente con VUE3.0: Comenzar
En este tutorial, aprenderemos cómo implementar Memoization en React. La memorización mejora el rendimiento al almacenar en caché los resultados de las llamadas a funciones y devolver esos resultados almacenados en caché cuando se necesitan nuevamente.
Cubriremos lo siguiente:
Este artículo asume que tiene un conocimiento básico de los componentes de clases y funciones en React.
Si desea consultar estos temas, puede consultar los componentes y accesorios de la documentación oficial de React
https://reactjs.org/docs/components-and-props.html
Antes de discutir los detalles de Memoization en React, primero echemos un vistazo a cómo React usa el DOM virtual para representar la interfaz de usuario.
Un DOM normal básicamente contiene un conjunto de nodos en forma de árbol. Cada nodo en el DOM representa un elemento de la interfaz de usuario. Cada vez que ocurre un cambio de estado en la aplicación, los nodos correspondientes para ese elemento de la interfaz de usuario y todos sus elementos secundarios se actualizan en el árbol DOM, lo que luego activa un rediseño de la interfaz de usuario.
Con la ayuda de algoritmos eficientes del árbol DOM, la actualización de los nodos es más rápida, pero el rediseño es lento y puede afectar el rendimiento cuando el DOM tiene una gran cantidad de elementos de la interfaz de usuario. Por lo tanto, se introdujo el DOM virtual en React.
Esta es una representación virtual del DOM real. Ahora, cada vez que hay algún cambio en el estado de la aplicación, React no actualiza el DOM real directamente, sino que crea un nuevo DOM virtual. Luego, React comparará este nuevo DOM virtual con el DOM virtual creado previamente, encontrará las diferencias (Nota del traductor: es decir, encontrará los nodos que deben actualizarse) y luego volverá a dibujar.
Según estas diferencias, el DOM virtual puede actualizar el DOM real de manera más eficiente. Esto mejora el rendimiento porque el DOM virtual no simplemente actualiza el elemento de la interfaz de usuario y todos sus elementos secundarios, sino que actualiza de manera efectiva solo los cambios mínimos y necesarios en el DOM real.
En la sección anterior, vimos cómo React usa un DOM virtual para realizar operaciones de actualización de DOM de manera eficiente para mejorar el rendimiento. En esta sección, presentaremos un ejemplo que explica la necesidad de utilizar Memoization para mejorar aún más el rendimiento.
Crearemos una clase principal que contiene un botón que incrementa una variable llamada count
. El componente principal también llama al componente secundario y le pasa parámetros. También agregamos console.log()
en el método render
:
//Parent.js clase Padre extiende React.Component { constructor(accesorios) { súper(accesorios); this.state = {cuenta: 0}; } manejarClick = () => { this.setState((estadoprev) => { return {cuenta: prevState.count + 1}; }); }; prestar() { console.log("Renderizado principal"); devolver ( <div className="Aplicación"> <button onClick={this.handleClick}>Incrementar</button> <h2>{este.estado.count}</h2> <Nombre del niño={"joe"} /> </div> ); } } exportar padre predeterminado;
el código completo de este ejemplo se puede ver en CodeSandbox.
Crearemos una clase Child
que acepte los parámetros pasados por el componente principal y los muestre en la interfaz de usuario:
//Child.js clase Niño extiende React.Component { prestar() { console.log("Renderizado hijo"); devolver ( <div> <h2>{this.props.name}</h2> </div> ); } } exportar niño predeterminado;
count
cambiará cada vez que hagamos clic en el botón en el componente principal. Dado que el estado ha cambiado, se ejecuta el método render
del componente principal.
Los parámetros pasados al componente secundario no cambian cada vez que se vuelve a renderizar el componente principal, por lo que el componente secundario no debería volver a renderizarse. Sin embargo, cuando ejecutamos el código anterior y continuamos incrementando count
, obtenemos el siguiente resultado:
Representación principal renderizado infantil Representación principal renderizado infantil Representación principal Representación secundaria.
Puede probar el ejemplo anterior en este entorno limitado y ver el resultado de la consola.
En el resultado, podemos ver que cuando el componente principal se vuelve a renderizar, el componente secundario se vuelve a renderizar incluso si los parámetros pasados al componente secundario permanecen sin cambios. Esto hará que el DOM virtual del componente secundario realice una verificación de diferencias con el DOM virtual anterior. Dado que no hay cambios en nuestros componentes secundarios y todos los accesorios no cambian al volver a renderizar, el DOM real no se actualizará.
Definitivamente es un beneficio de rendimiento que el DOM real no se actualice innecesariamente, pero podemos ver que se crea un nuevo DOM virtual y se realiza una verificación de diferencias incluso cuando no hay cambios reales en los componentes secundarios. Para componentes pequeños de React, este costo de rendimiento es insignificante, pero para componentes grandes, el impacto en el rendimiento puede ser significativo. Para evitar esta repetición y verificación diferencial del DOM virtual, utilizamos Memoization.
En el contexto de las aplicaciones React, la memorización es un medio por el cual cada vez que el componente principal se vuelve a renderizar, el componente secundario solo se vuelve a renderizar cuando los accesorios de los que depende cambian. Si no hay cambios en los accesorios de los que depende el componente secundario, no ejecutará el método de renderizado y devolverá los resultados almacenados en caché. Dado que el método de renderizado no se ejecuta, no habrá creación de DOM virtual ni verificación diferencial, lo que mejorará el rendimiento.
Ahora, veamos cómo implementar Memoization en componentes de clase y función para evitar esta repetición innecesaria.
Para implementar la memorización en un componente de clase, usaremos React.PureComponent. React.PureComponent
implementa shouldComponentUpdate(), que realiza una comparación superficial state
y props
y solo vuelve a renderizar el componente de React si los accesorios o el estado cambian.
Cambie el componente secundario a un código como este:
//Child.js class Child extiende React.PureComponent { // Aquí cambiamos React.Component a React.PureComponent prestar() { console.log("Renderizado hijo"); devolver ( <div> <h2>{this.props.name}</h2> </div> ); } } exportar niño predeterminado;
el código completo para este ejemplo se muestra en este entorno limitado.
El componente principal permanece sin cambios. Ahora, cuando incrementamos count
en el componente principal, la salida en la consola se ve así:
Representación principal renderizado infantil Representación principal Renderizado principal
Para el primer renderizado, llama al método render
tanto del componente principal como del componente secundario.
Para cada repetición después de aumentar count
, solo se llama a la función render
del componente principal. Los componentes secundarios no se volverán a renderizar.
Para implementar memorización en componentes de funciones, usaremos React.memo(). React.memo()
es un componente de orden superior (HOC) que realiza un trabajo similar a PureComponent
para evitar una repetición innecesaria.
Aquí está el código para el componente de función:
//Child.js función de exportación Niño (accesorios) { console.log("Renderizado hijo"); devolver ( <div> <h2>{props.nombre}</h2> </div> ); } export default React.memo(Child); // Aquí agregamos HOC al componente secundario para implementar Memoization
y también convertimos el componente principal en un componente de función, como se muestra a continuación:
//Parent.js exportar función predeterminada Parent() { const [recuento, setCount] = useState(0); const handleClick = () => { setCount(cuenta + 1); }; console.log("Renderizado principal"); devolver ( <div> <button onClick={handleClick}>Incrementar</button> <h2>{cuenta}</h2> <Nombre del niño={"joe"} /> </div> ); }
El código completo de este ejemplo se puede ver en este sandbox.
Ahora, cuando incrementamos count
en el componente principal, se enviará lo siguiente a la consola:
Representación principal renderizado infantil Representación principal Representación principal
el renderizado principalReact.memo()
En el ejemplo anterior, vemos que cuando usamos React.memo()
HOC en un componente secundario, el componente secundario no se vuelve a renderizar aunque se vuelva a renderizar el componente principal.
Sin embargo, una pequeña cosa a tener en cuenta es que si pasamos una función como parámetro al componente secundario, el componente secundario se volverá a representar incluso después de usar React.memo()
. Veamos un ejemplo de esto.
Cambiaremos el componente principal como se muestra a continuación. Aquí agregamos una función de controlador y la pasamos como parámetro al componente secundario:
//Parent.js exportar función predeterminada Parent() { const [recuento, setCount] = useState(0); const handleClick = () => { setCount(cuenta + 1); }; controlador constante = () => { console.log("handler"); // La función del controlador aquí se pasará al componente secundario}; console.log("Renderizado principal"); devolver ( <div className="Aplicación"> <button onClick={handleClick}>Incrementar</button> <h2>{cuenta}</h2> <Nombre del niño={"joe"} childFunc={handler} /> </div> ); }
El código del componente secundario permanecerá como está. No usaremos funciones pasadas desde componentes principales en componentes secundarios:
//Child.js función de exportación Niño (accesorios) { console.log("Renderizado hijo"); devolver ( <div> <h2>{props.nombre}</h2> </div> ); } export default React.memo(Child);
ahora, cuando incrementamos count
en el componente principal, se vuelve a renderizar y se vuelve a renderizar el componente secundario al mismo tiempo, aunque no haya cambios en los parámetros pasados.
Entonces, ¿qué causa que el subcomponente se vuelva a renderizar? La respuesta es que cada vez que el componente principal se vuelve a renderizar, se crea una nueva función handler
y se pasa al componente secundario. Ahora, debido a que la función handle
se recrea en cada repetición, el componente secundario encontrará que handler
ha cambiado al realizar una comparación superficial de los accesorios y volverá a renderizar el componente secundario.
A continuación, cubriremos cómo solucionar este problema.
useCallback()
para evitar más renderizacionesEl principal problema con la renderización de componentes secundarios es que se recrea la función handler
, lo que cambia la referencia pasada al componente secundario. Por lo tanto, necesitamos una manera de evitar esta duplicación. Si la función handler
no se vuelve a crear, la referencia a la función handler
no cambia, por lo que el componente secundario no se vuelve a representar.
Para evitar tener que recrear la función cada vez que se renderiza el componente principal, usaremos un React Hook llamado useCallback(). Los ganchos se introdujeron en React 16. Para obtener más información sobre los ganchos, puede consultar la documentación oficial de los ganchos de React o consultar "React Hooks: cómo comenzar y crear el suyo propio".
El gancho useCallback()
toma dos parámetros: una función de devolución de llamada y una lista de dependencias.
El siguiente es un ejemplo de useCallback()
:
const handleClick = useCallback(() => {
.
//Haz algo }, [x,y]);
aquí, useCallback()
se agrega a la función handleClick()
. El segundo parámetro [x, y]
puede ser una matriz vacía, una dependencia única o una lista de dependencias. La función handleClick()
solo se recrea cuando cambia alguna de las dependencias mencionadas en el segundo parámetro.
Si las dependencias mencionadas en useCallback()
no han cambiado, entonces se devuelve la versión memorizada de la función de devolución de llamada mencionada como primer argumento. Cambiaremos el componente principal para usar useCallback()
en los controladores pasados al componente secundario:
//Parent.js exportar función predeterminada Parent() { const [recuento, setCount] = useState(0); const handleClick = () => { setCount(cuenta + 1); }; const handler = useCallback(() => { // Utilice useCallback() para la función del controlador console.log("controlador"); }, []); console.log("Renderizado principal"); devolver ( <div className="Aplicación"> <button onClick={handleClick}>Incrementar</button> <h2>{cuenta}</h2> <Nombre del niño={"joe"} childFunc={handler} /> </div> ); }
El código del componente secundario permanecerá como está.
El código completo para este ejemplo se encuentra en este entorno limitado.
Cuando incrementamos count
en el componente principal del código anterior, podemos ver el siguiente resultado:
Representación principal renderizado infantil Representación principal Representación principal Representación principal
Debido a que usamos useCallback()
para handler
en el componente principal, la función handler
no se recreará cada vez que se vuelva a representar el componente principal, y la versión de Memoización del handler
se pasará al componente secundario. El componente secundario hará una comparación superficial y notará que la referencia a la función handler
no ha cambiado, por lo que no llamará al método render
.
la memorización es un buen medio para evitar la repetición innecesaria de componentes cuando su estado o accesorios no han cambiado, mejorando así el rendimiento de las aplicaciones React. Podría considerar agregar Memoization a todos sus componentes, pero esa no es necesariamente la forma de crear componentes React de alto rendimiento. La memorización solo debe usarse si el componente:
En este tutorial entendimos:
React.memo()
para componentes de función y React.PureComponent
para componentes de claseReact.memo()
useCallback()
evitar problemas de renderizado cuando las funciones se pasan como accesorios a componentes secundarios.¡Espero que esta introducción a React Memoization le resulte útil!
Dirección original: https://www.sitepoint.com/implement-memoization-in-react-to-improve-performance/Autor
original: Nida Khan