Comment utiliser React.memo pour optimiser les performances
J'ai créé une partie d'échecs en utilisant une interface React. À l'origine, tous les mouvements étaient mis en œuvre apparemment instantanément, mais après avoir ajouté des fonctionnalités supplémentaires telles que la modification de l'arrière-plan de l'en-tête pour refléter à qui appartient le tour (fond blanc ou noir selon le mouvement de son blanc) et d'autres fonctionnalités similaires, mon application a sensiblement ralenti. Je soupçonne que ce sont ces types de déclarations 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")
}
qui sont responsables car la console.log imprimant les accessoires imprimera sa déclaration 8 fois par coup (à l'origine, c'était deux fois)
J'utilise des composants fonctionnels plutôt que des classes et mes recherches sur l'optimisation m'ont conduit aux solutions suivantes:
React Hooks - Comment implémenter shouldComponentUpdate?
Dois-je envelopper tous mes composants avec React.memo () s'il n'attend aucun accessoire?
Comment utiliser shouldComponentUpdate avec React Hooks?
tous pointant vers des cas simples de React.memo
mais il me manque évidemment quelque chose parce que lorsque je tente de l'implémenter, tout ce que j'obtiens est une pléthore de `` accessoires '', ce ne sont pas des erreurs définies (une pour chaque fois que j'utilise des accessoires, ce qui est beaucoup).
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;
Étant donné que les accessoires de ce composant ne changent que lorsque le virage change (props.status.white), j'ai pensé que ce serait un bon endroit pour essayer de réduire les rendus inutiles, mais toutes les solutions que je vois sont très simples. N'est-il pas possible d'utiliser React.memo quand il y a une utilisation répandue d'accessoires comme celui-ci?
Comment optimiser les performances tout en conservant l'accès aux accessoires?
Réponses
Tout d'abord, vous devez garder à l'esprit le test des performances du frontend en général.
Vous ne laissez jamais les devtools ouverts pendant que vous vérifiez les performances, les activités dans les devtools, diminuez beaucoup les performances de votre application.
Deuxièmement, les deux useMemo
et useCallback
ne vous apporteront aucune amélioration des performances si vous n'effectuez pas un rendu exhaustif, les accessoires de rendu possibles sont rendus avant et le plus important, si vous ne rendez pas d'autres composants personnalisés à l'intérieur de votre composant personnalisé, probablement, vous ne le faites pas. besoin d'utiliser ces crochets aussi.
Alors, quelle situation devons-nous utiliser useCallback
et useMemo
, pour améliorer les performances?
Avant de répondre à cela, vous devez connaître la différence entre les types valeur et les types référence.
Les types de valeur sont immuables, vous pouvez donc les attribuer, les modifier et les utiliser en toute sécurité sans vous soucier de la propriété et du garbage collector.
Les types de littéraux primitifs dans JS sont des types valeur:
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
Ceci est un exemple très simple de la façon dont nous pouvons créer la même chaîne en utilisant deux approches différentes.
Le premier était de créer une chaîne en utilisant une chaîne littérale. Deuxièmement en utilisant une instance de la classe String.
Si nous faisons quelque chose comme ça:
a === b // true
Le ===
compare la valeur de a
à la valeur de b
.
Cependant, quelque chose de différent se produit lorsque nous faisons quelque chose comme ça:
c === d // true
Cela renvoie également true
mais les ===
travaux sont différents ici, au lieu de comparer les valeurs, il compare la référence de c est strictement égale à la référence de d . Nous ne pouvons pas comparer si la valeur de c
est strictement égale à la valeur de d
simplement utiliser l' ===
opérateur, puisque les deux sont des types de référence de String, nous devrions comparer les deux comme ça:
// toString() in this case works as well but valueOf is more semantic in this case
c.valueOf() === d.valueOf()
Pensez à a
et b
ne pointe pas vers une référence en mémoire, mais d
référence à la même référence créée dans c
.
Alors maintenant, dans cet esprit, revenons à la question de savoir dans quelle situation ces hooks améliorent les performances d'une application React.
Pour aider React à comparer les valeurs de référence , telles que les types Function, Array et Object par exemple, nous utilisons useCallback
ou useMemo
pour encapsuler ces références dans un nouveau type que React peut comparer et décider si la valeur de la référence a été modifiée.
Ainsi, si vous ne restituer que le niveau le plus bas de votre hiérarchie, ces hooks ne vous aideront probablement pas à résoudre les problèmes de performances.
Cependant, si vous l'utilisez dans une partie de votre application qui traite des types de référence et que vous l'affichez fréquemment. C'est une belle opportunité d'utiliser cela et aide React à savoir si ces références ont été modifiées ou non pour rendre ou non le reste ou la hiérarchie.
En résumé, useMemo
et useCallback
sont utilisés pour aider React à savoir si un type de référence a changé sa valeur pour rendre le composant.