Cómo utilizar React.memo para optimizar el rendimiento
Hice un juego de ajedrez usando una interfaz React. Originalmente, cualquier movimiento se implementaba aparentemente de forma instantánea, pero después de agregar características adicionales como cambiar el fondo del encabezado para reflejar de quién es el turno (fondo blanco o negro dependiendo de si es el movimiento blanco) y otras características similares, mi aplicación se ha ralentizado notablemente. Sospecho que son este tipo de declaraciones if-
const Details = props => {
console.log(props.status);
let [backGround, setBackGround] = useState("w details")
const history = useHistory();
if (props.status.white && backGround === "bl details"){
setBackGround("w details")
} else if (!props.status.white && backGround === "w details"){
setBackGround("bl details")
}
que son responsables porque el console.log imprimiendo los accesorios imprimirá su declaración 8 veces por movimiento (originalmente era dos veces)
Estoy usando componentes funcionales en lugar de clases y mi investigación sobre optimización me llevó a las siguientes soluciones:
React Hooks - ¿Cómo implemento shouldComponentUpdate?
¿Debo envolver todos mis componentes con React.memo () si no espera ningún accesorio?
¿Cómo usar shouldComponentUpdate con React Hooks?
todos apuntan a casos simples de React.memopero obviamente me falta algo porque cuando intento implementarlo, todo lo que obtengo es una plétora de 'accesorios' que no son errores definidos (uno por cada vez que uso accesorios, que es mucho).
Details.jsx
import React, { useState } from 'react';
import "../App.css"
import DataService from '../service/DataService';
import { useHistory } from 'react-router-dom';
let [backGround, setBackGround] = useState("w details")
const Details = React.memo(props => {if (props.status.white){setBackGround("w details")} else {setBackGround("bl details")}}, (prevProps, props) => prevProps.white === props.white); {
console.log(props.status);
const history = useHistory();
const undo = () => {
DataService.undo()
.then(res => {
console.log(res);
props.setTheBoard(res.data);
props.changeTurn();
})
.catch(err => {
console.log(err);
window.alert(err.response.data.errMessage)
})
}
const restart = () => {
DataService.restartGame()
.then(res => {
console.log(res);
props.setTheBoard(res.data);
props.changeTurn(true);
})
.catch(err => {
console.log(err);
window.alert(err.response.data.errMessage)
})
}
const newGame = () => {
history.push('/');
}
return (
<div className={backGround} >
{props.status.active ? <h2>It is {props.status.playerName}'s turn</h2> :
<div>
<h1 className="check">Game Over!</h1>
<button className="tooltip" onClick={restart}>RESTART<span className="tooltiptext">Play another game vs the same opponent</span></button>
<button className="tooltip" onClick={newGame}>NEW GAME<span className="tooltiptext">Play a game vs a different opponent</span></button>
</div>}
{props.status.active &&
<div>
{props.isMove ? <button className="detailButtons" onClick={props.specialMove}>Special Move</button> : <button className="detailButtons" onClick={() => props.endTheGame(true)}>Forfeit</button> }
{props.isMove ? <button className="detailButtons" onClick={props.unselect}>Unselect Piece</button> : <button className="detailButtons" onClick={() => props.endTheGame(false)}>Draw</button> }
{props.isMove ? <button className="detailButtons">Toggle Sidebar</button> : props.undo && <button className="detailButtons" onClick={() => undo()}>Undo</button> }
{props.status.check && <h1 className="check">You must move out of check!</h1>}
</div> }
</div>
);
}
export default Details;
Dado que los accesorios en este componente solo cambian cuando cambia el turno (props.status.white), pensé que este sería un buen lugar para tratar de reducir las renovaciones innecesarias, pero todas las soluciones que veo son muy simples. ¿No es posible usar React.memo cuando hay un uso generalizado de accesorios como este?
¿Cómo optimizo el rendimiento sin dejar de tener acceso a los accesorios?
Respuestas
Primero, algo que debe tener en cuenta sobre las pruebas de rendimiento en frontend en general.
Usted nunca se deja devtools abren mientras estás rendimiento de cheques, las actividades en las devtools, disminuye mucho el rendimiento de la aplicación.
En segundo lugar, ambos useMemoy useCallbackno le brindarán ninguna mejora de rendimiento si no está renderizando de manera exhaustiva, los accesorios de renderizado que es posible se renderizan antes y, lo que es más importante, si no está renderizando otros componentes personalizados dentro de su componente personalizado, probablemente no lo haga. Necesito usar esos ganchos también.
Entonces, ¿qué situación deberíamos utilizar useCallbacky useMemo, para mejorar el rendimiento?
Antes de responder eso, debe conocer la diferencia entre los tipos de valor y los tipos de referencia.
Los tipos de valor son inmutables, por lo que puede asignarlos, modificarlos y usarlos de manera segura sin preocuparse por la propiedad y el recolector de basura.
Los tipos de literales primitivos en JS son tipos de valor:
let a = "Hello"
let b = a
// from here, b doesn't own a reference, because a is a value type
let c = new String("Hello")
let d = c
// now d owns c reference because c was initialized as an instance of String class
Este es un ejemplo muy simple de cómo podemos crear la misma cadena usando dos enfoques diferentes.
El primero fue crear una cadena usando una cadena literal. Segundo usando una instancia de la clase String.
Si hacemos algo así:
a === b // true
El ===compara el valor de acontra la el valor en b.
Sin embargo, sucede algo diferente cuando hacemos algo así:
c === d // true
Esto también devuelve, truepero las ===obras son diferentes aquí, en lugar de comparar valores, compara la referencia de c es estrictamente igual a la referencia de d . No podemos comparar si el valor de ces estrictamente igual al valor de dusar el ===operador, ya que ambos son tipos de referencia de String, deberíamos comparar ambos así:
// toString() in this case works as well but valueOf is more semantic in this case
c.valueOf() === d.valueOf()
Piensa en una referencia en la memoria ay bno apunta a ella, sino da la misma referencia creada en c.
Así que ahora, con esto en mente, volvamos a la pregunta sobre qué situación esos ganchos mejoran el rendimiento de una aplicación React.
Para ayudar a React a comparar valores de referencia , como los tipos de función, matriz y objeto, por ejemplo, usamos useCallbacko useMemopara encapsular esas referencias en un nuevo tipo que React puede comparar y decidir si se cambió el valor de la referencia .
Entonces, si está renderizando solo el nivel más bajo de su jerarquía, probablemente esos ganchos no lo ayudarán a resolver problemas de rendimiento.
Sin embargo, si lo usa en una parte de su aplicación que se ocupa de tipos de referencia y la está renderizando con frecuencia. Es una buena oportunidad para usar eso y ayuda a React a saber si esas referencias se han cambiado o no para representar o no el resto o la jerarquía.
En resumen, useMemoy useCallbackse utilizan para ayudar a React a averiguar si algún tipo de referencia ha cambiado su valor para representar el componente.