Verwendung von React.memo zur Optimierung der Leistung
Ich habe ein Schachspiel mit einem React-Frontend gemacht. Ursprünglich wurden alle Bewegungen scheinbar sofort implementiert, aber nachdem ich zusätzliche Funktionen wie das Ändern des Hintergrunds der Kopfzeile hinzugefügt habe, um zu reflektieren, wer an der Reihe ist (weißer oder schwarzer Hintergrund, je nachdem, ob sich Weiß bewegt), und andere ähnliche Funktionen, hat sich meine App merklich verlangsamt. Ich vermute, es sind diese Arten von if-Anweisungen.
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")
}
das ist verantwortlich, weil das console.log, das die Requisiten druckt, seine Anweisung 8 Mal pro Zug druckt (ursprünglich war es zweimal)
Ich verwende eher funktionale Komponenten als Klassen und meine Forschungen zur Optimierung führten mich zu den folgenden Lösungen:
React Hooks - Wie implementiere ich shouldComponentUpdate?
Sollte ich alle meine Komponenten mit React.memo () umschließen, wenn keine Requisiten erwartet werden?
Wie verwende ich shouldComponentUpdate mit React Hooks?
Alles deutet auf einfache Fälle von hin, React.memo
aber mir fehlt offensichtlich etwas, denn wenn ich versuche, es zu implementieren, bekomme ich nur eine Fülle von 'Requisiten', keine definierten Fehler (einer für jedes Mal, wenn ich Requisiten benutze, was sehr viel ist).
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;
Da sich die Requisiten in dieser Komponente nur ändern, wenn sich die Drehung ändert (props.status.white), dachte ich, dies wäre ein guter Ort, um unnötige Renderings zu reduzieren, aber alle Lösungen, die ich dafür sehe, sind sehr einfach. Ist es nicht möglich, React.memo zu verwenden, wenn solche Requisiten weit verbreitet sind?
Wie optimiere ich die Leistung, während ich weiterhin Zugriff auf Requisiten habe?
Antworten
Zunächst etwas, das Sie beim Testen der Leistung im Frontend im Allgemeinen beachten müssen.
Sie lassen devtools niemals offen, während Sie die Leistung und Aktivitäten in den devtools überprüfen und die Leistung Ihrer App erheblich verringern.
Zweitens, beides useMemo
und useCallback
keine Leistungsverbesserung, wenn Sie nicht vollständig rendern. Möglicherweise mögliche Rendering-Requisiten werden vorher gerendert, und am wichtigsten, wenn Sie keine anderen benutzerdefinierten Komponenten in Ihrer benutzerdefinierten Komponente rendern, wahrscheinlich nicht müssen diese Haken auch verwenden.
Also, die Situation sollten wir nutzen useCallback
und useMemo
, um die Leistung zu verbessern?
Bevor Sie darauf antworten, sollten Sie den Unterschied zwischen Werttypen und Referenztypen kennen.
Werttypen sind unveränderlich, sodass Sie sie sicher zuweisen, ändern und verwenden können, ohne sich um den Besitz und den Garbage Collector sorgen zu müssen.
Die primitiven Literaltypen in JS sind Werttypen:
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
Dies ist ein sehr einfaches Beispiel dafür, wie wir dieselbe Zeichenfolge mit zwei verschiedenen Ansätzen erstellen können.
Das erste war das Erstellen einer Zeichenfolge mit einer Literalzeichenfolge. Zweitens mit einer Instanz der String-Klasse.
Wenn wir so etwas tun:
a === b // true
Der ===
vergleicht den Wert des a
gegenüber dem Wert in b
.
Wenn wir so etwas tun, passiert jedoch etwas anderes:
c === d // true
Dies kehrt ebenfalls zurück, true
aber die ===
hier unterschiedlichen Arbeiten, anstatt Werte zu vergleichen, vergleichen die Referenz von c streng gleich der Referenz von d . Wir können nicht vergleichen, ob der Wert von c
genau dem Wert der d
einfachen Verwendung des ===
Operators entspricht, da beide Referenztypen von Zeichenfolgen sind, sollten wir beide folgendermaßen vergleichen:
// toString() in this case works as well but valueOf is more semantic in this case
c.valueOf() === d.valueOf()
Denken Sie über eine Referenz im Speicher nach a
und b
verweisen Sie nicht darauf, sondern d
auf dieselbe Referenz, die in erstellt wurde c
.
In diesem Sinne kehren wir nun zu der Frage zurück, in welcher Situation diese Hooks die Leistung einer React-App verbessern.
Um Hilfe React vergleichen Referenzwerte, wie Funktion, Array und Objekttypen zum Beispiel verwenden wir useCallback
oder kapseln diese Verweise in eine neue Art , die vergleichen und entscheiden , reagieren kann , wenn der Wert des Bezugs geändert wurde.useMemo
Wenn Sie also nur die unterste Ebene Ihrer Hierarchie rendern, helfen Ihnen diese Hooks wahrscheinlich nicht bei der Lösung von Leistungsproblemen.
Wenn Sie dies jedoch in einem Teil Ihrer App verwenden, der sich mit Referenztypen befasst, und Sie es häufig rendern. Es ist eine gute Gelegenheit, dies zu nutzen und React zu helfen, zu wissen, ob diese Referenzen geändert wurden oder nicht, um den Rest oder die Hierarchie zu rendern oder nicht.
Im Lebenslauf useMemo
und useCallback
werden verwendet, um React dabei zu helfen, herauszufinden, ob ein Referenztyp seinen Wert geändert hat, um die Komponente zu rendern.