ReactJS - Wird das Rendering jedes Mal aufgerufen, wenn "setState" aufgerufen wird?

503

Rendert React bei jedem setStateAufruf alle Komponenten und Unterkomponenten neu ?

Wenn ja warum? Ich dachte, die Idee war, dass React nur so wenig wie nötig rendert - wenn sich der Zustand ändert.

Im folgenden einfachen Beispiel werden beide Klassen erneut gerendert, wenn auf den Text geklickt wird, obwohl sich der Status bei nachfolgenden Klicks nicht ändert, da der onClick-Handler stateden Wert immer auf denselben Wert setzt:

this.setState({'test':'me'});

Ich hätte erwartet, dass das Rendern nur stattfinden würde, wenn sich die stateDaten geändert hätten.

Hier ist der Code des Beispiels als JS-Geige und eingebettetes Snippet:

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

[1]: http://jsfiddle.net/fp2tncmb/2/
Brad Parks
quelle
Ich hatte das gleiche Problem, ich kenne die genaue Lösung nicht. Aber ich hatte die unerwünschten Codes von der Komponente entfernt, die wie gewohnt zu funktionieren begann.
Jaison
Ich möchte darauf hinweisen, dass in Ihrem Beispiel - da die Gestaltung Ihres Elements nicht nur von einem eindeutigen Status abhängt - das Aufrufen setState()selbst mit Dummy-Daten dazu führt, dass das Element anders gerendert wird, also würde ich ja sagen. Auf jeden Fall sollte es versuchen, Ihr Objekt neu zu rendern, wenn sich etwas geändert hat, da sonst Ihre Demo - vorausgesetzt, es war das beabsichtigte Verhalten - nicht funktionieren würde!
Tadhg McDonald-Jensen
Sie haben vielleicht Recht @ TadhgMcDonald-Jensen - aber nach meinem Verständnis hätte React es das erste Mal gerendert (da sich der Status beim ersten Mal von nichts zu etwas ändert) und musste es dann nie wieder rendern. Ich habe mich natürlich geirrt - da React anscheinend erfordert, dass Sie Ihre eigene shouldComponentUpdateMethode schreiben , von der ich angenommen habe, dass eine einfache Version bereits in React selbst enthalten sein muss. Klingt so, als würde die in reag enthaltene Standardversion einfach zurückkehren true- wodurch die Komponente jedes Mal neu gerendert werden muss.
Brad Parks
Ja, aber es muss nur im virtuellen DOM neu gerendert werden. Dann ändert es das tatsächliche DOM nur, wenn die Komponente anders gerendert wird. Aktualisierungen des virtuellen DOM sind normalerweise vernachlässigbar (zumindest im Vergleich zum Ändern von Elementen auf dem tatsächlichen Bildschirm). Rufen Sie Render jedes Mal auf, wenn eine Aktualisierung erforderlich ist, und stellen Sie dann fest, dass keine Änderung vorgenommen wurde. Dies ist nicht sehr teuer und sicherer als die Annahme, dass es dasselbe rendern sollte.
Tadhg McDonald-Jensen

Antworten:

570

Rendert React bei jedem Aufruf von setState alle Komponenten und Unterkomponenten neu?

Standardmäßig - ja.

Es gibt eine Methode boolean shouldComponentUpdate (Objekt nextProps, Objekt nextstate) , wobei jede Komponente dieser Methode hat und es ist verantwortlich , um zu bestimmen „sollte das Komponenten - Update (run machen - Funktion)?“ Jedes Mal, wenn Sie den Status ändern oder neue Requisiten von der übergeordneten Komponente übergeben.

Sie können Ihre eigene Implementierung der shouldComponentUpdate- Methode für Ihre Komponente schreiben , die Standardimplementierung gibt jedoch immer true zurück. Dies bedeutet , dass die Renderfunktion immer erneut ausgeführt wird.

Zitat aus offiziellen Dokumenten http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate

Standardmäßig gibt shouldComponentUpdate immer true zurück, um subtile Fehler zu vermeiden, wenn der Status an Ort und Stelle mutiert ist. Wenn Sie jedoch darauf achten, den Status immer als unveränderlich zu behandeln und von Requisiten und Status in render () schreibgeschützt zu sein, können Sie shouldComponentUpdate mit überschreiben Eine Implementierung, die die alten Requisiten und den Zustand mit ihren Ersetzungen vergleicht.

Nächster Teil Ihrer Frage:

Wenn ja warum? Ich dachte, die Idee war, dass React nur so wenig wie nötig rendert - wenn sich der Zustand ändert.

Es gibt zwei Schritte, die wir "Rendern" nennen können:

  1. Virtuelles DOM-Rendern: Wenn die Rendermethode aufgerufen wird, wird eine neue virtuelle Dom- Struktur der Komponente zurückgegeben. Wie ich schon erwähnt, dies machen wird Methode aufgerufen immer , wenn Sie anrufen setState () , weil shouldComponentUpdate immer true zurückgibt standardmäßig. Daher gibt es hier in React standardmäßig keine Optimierung.

  2. Native DOM-Renderings: React ändert echte DOM-Knoten in Ihrem Browser nur, wenn sie im virtuellen DOM geändert wurden und so wenig wie nötig - dies ist die großartige Funktion von React, die die echte DOM-Mutation optimiert und React schnell macht.

Petr
quelle
47
Tolle Erklärung! Und das einzige, was React in diesem Fall wirklich noch zum Leuchten bringt, ist, dass es das reale DOM nur ändert, wenn das virtuelle DOM geändert wurde. Wenn meine Renderfunktion also zweimal hintereinander dasselbe zurückgibt, wird das echte DOM überhaupt nicht geändert ... Danke!
Brad Parks
1
Ich denke, @tungd hat recht - siehe seine Antwort unten. Es geht auch um eine Eltern-Kind-Beziehung zwischen Komponenten. Wenn TimeInChild beispielsweise ein Geschwister von Main ist, wird sein render () nicht unnötig aufgerufen.
Gvlax
2
Wenn Ihr Status ein relativ großes Javascript-Objekt enthält (~ 1k Gesamteigenschaften), das als großer Baum von Komponenten (insgesamt ~ 100) gerendert wird ... sollten Sie die Renderfunktionen den virtuellen Dom erstellen lassen oder sollten Sie dies vor dem Festlegen tun den Zustand, vergleichen Sie den neuen Zustand mit dem alten manuell und rufen setStateSie nur an, wenn Sie feststellen, dass es einen Unterschied gibt? Wenn ja, wie geht das am besten? Vergleichen Sie die JSON-Zeichenfolgen, konstruieren und vergleichen Sie Objekt-Hashes, ...?
Vincent Sels
1
@Petr, aber obwohl reagieren, den virtuellen Dom zu rekonstruieren, wird der Browser-Dom nicht berührt, wenn der alte virtuelle Dom mit dem neuen virtuellen Dom identisch ist, nicht wahr?
Jaskey
3
Sehen Sie sich auch die Verwendung von React.PureComponent an ( reactjs.org/docs/react-api.html#reactpurecomponent ). Es wird nur aktualisiert (neu gerendert), wenn sich der Status oder die Requisiten der Komponente tatsächlich geändert haben. Beachten Sie jedoch, dass der Vergleich flach ist.
Debater
105

Nein, React rendert nicht alles, wenn sich der Status ändert.

  • Immer wenn eine Komponente verschmutzt ist (ihr Status wurde geändert), werden diese Komponente und ihre untergeordneten Komponenten neu gerendert. Dies dient bis zu einem gewissen Grad dazu, so wenig wie möglich neu zu rendern. Das einzige Mal, wenn das Rendern nicht aufgerufen wird, wird ein Zweig in einen anderen Stamm verschoben, wo wir theoretisch nichts erneut rendern müssen. In Ihrem Beispiel TimeInChildhandelt es sich um eine untergeordnete Komponente von Main, sodass sie auch neu gerendert wird, wenn sich der Status Mainändert.

  • React vergleicht keine Statusdaten. Wenn setStatees aufgerufen wird, markiert es die Komponente als fehlerhaft (was bedeutet, dass sie neu gerendert werden muss). Es ist wichtig zu beachten, dass, obwohl die renderMethode der Komponente aufgerufen wird, das reale DOM nur aktualisiert wird, wenn sich die Ausgabe vom aktuellen DOM-Baum unterscheidet (auch bekannt als Unterschied zwischen dem virtuellen DOM-Baum und dem DOM-Baum des Dokuments). In Ihrem Beispiel statehat sich der Zeitpunkt der letzten Änderung geändert, obwohl sich die Daten nicht geändert haben. Dadurch unterscheidet sich das virtuelle DOM vom DOM des Dokuments, weshalb der HTML-Code aktualisiert wird.

Tungd
quelle
Ja, das ist die richtige Antwort. Ändern Sie als Experiment die letzte Zeile als React.renderComponent (<div> <Main /> <TimeInChild /> </ div>, document.body) . und entfernen <TimeInChild /> aus dem Körper machen () der Hauptkomponente. Das render () von TimeInChild wird standardmäßig nicht aufgerufen, da es kein untergeordnetes Element von Main mehr ist.
Gvlax
Danke, solche Sachen sind ein bisschen knifflig, deshalb haben die React-Autoren empfohlen, dass die render()Methode "rein" sein sollte - unabhängig vom äußeren Zustand.
Tungd
3
@tungd, was bedeutet das some branch is moved to another root? Wie nennst branchdu Wie nennst rootdu
Grün
2
Ich bevorzuge wirklich die Antwort, die als die richtige markiert ist. Ich verstehe Ihre Interpretation von 'Rendern' auf der nativen, 'realen' Seite ... aber wenn Sie es von der reaktionsnativen Seite betrachten, müssen Sie sagen, dass es wieder rendert. Zum Glück ist es klug genug, festzustellen, was sich wirklich geändert hat, und nur diese Dinge zu aktualisieren. Diese Antwort könnte für neue Benutzer verwirrend sein, zuerst sagen Sie nein, und dann erklären Sie, dass Dinge gerendert werden ...
WiRa
1
@tungd können Sie bitte Green Frage erklärenwhat does it mean some branch is moved to another root? What do you call branch? What do you call root?
madhu131313
7

Obwohl dies in vielen anderen Antworten hier angegeben ist, sollte die Komponente entweder:

  • Implementieren shouldComponentUpdate, um nur zu rendern, wenn sich Status oder Eigenschaften ändern

  • Wechseln Sie zur Erweiterung einer PureComponent , die bereits shouldComponentUpdateintern eine Methode für flache Vergleiche implementiert .

Hier ist ein Beispiel shouldComponentUpdate, das nur für diesen einfachen Anwendungsfall und Demonstrationszwecke funktioniert. Wenn dies verwendet wird, wird die Komponente nicht mehr bei jedem Klick neu gerendert und beim ersten Anzeigen und nach einmaligem Klicken gerendert.

var TimeInChild = React.createClass({
    render: function() {
        var t = new Date().getTime();

        return (
            <p>Time in child:{t}</p>
        );
    }
});

var Main = React.createClass({
    onTest: function() {
        this.setState({'test':'me'});
    },

    shouldComponentUpdate: function(nextProps, nextState) {
      if (this.state == null)
        return true;
  
      if (this.state.test == nextState.test)
        return false;
        
      return true;
  },

    render: function() {
        var currentTime = new Date().getTime();

        return (
            <div onClick={this.onTest}>
            <p>Time in main:{currentTime}</p>
            <p>Click me to update time</p>
            <TimeInChild/>
            </div>
        );
    }
});

ReactDOM.render(<Main/>, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.0/react-dom.min.js"></script>

Brad Parks
quelle
6

Ja. Es ruft die render () -Methode jedes Mal auf, wenn wir stattdessen setState aufrufen, wenn "shouldComponentUpdate" false zurückgibt.

lakmal_sathyajith
quelle
7
Bitte seien Sie ausführlicher
Karthick Ramesh
3

Ein weiterer Grund für "verlorenes Update" kann der nächste sein:

Wenn es das Problem ist, kann U vermeiden, den Status während der Aktualisierung festzulegen. Sie sollten den Statusparameterwert wie folgt überprüfen

static getDerivedStateFromProps(props: TimeCorrectionProps, state: TimeCorrectionState): TimeCorrectionState {
   return state ? state : {disable: false, timeCorrection: props.timeCorrection};
}

Eine andere Lösung besteht darin, dem Status eine initialisierte Eigenschaft hinzuzufügen und diese beim ersten Mal einzurichten (wenn der Status auf einen Wert ungleich Null initialisiert wird).

Zoltán Krizsán
quelle
0

Nicht alle Komponenten.

Die stateIn-Komponente sieht aus wie die Quelle des Wasserfalls des Zustands der gesamten APP.

Die Änderung erfolgt also dort, wo der setState aufgerufen hat. Der Baum von rendersdann wird von dort gerufen. Wenn Sie eine reine Komponente verwendet haben, renderwird diese übersprungen.

Singhi John
quelle