Verwendung von React.memo zur Optimierung der Leistung

Nov 21 2020

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.memoaber 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

4 JanCássio Nov 21 2020 at 02:31

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 useMemound useCallbackkeine 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 useCallbackund 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 agegenüber dem Wert in b.

Wenn wir so etwas tun, passiert jedoch etwas anderes:

c === d // true

Dies kehrt ebenfalls zurück, trueaber 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 cgenau dem Wert der deinfachen 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 aund bverweisen Sie nicht darauf, sondern dauf 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 useCallbackoder 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 useMemound useCallbackwerden verwendet, um React dabei zu helfen, herauszufinden, ob ein Referenztyp seinen Wert geändert hat, um die Komponente zu rendern.