Wie kann ich eine Reaktionskomponente zurücksetzen, die alle transitiv erreichbaren Zustände enthält?

93

Ich habe gelegentlich reaktionsfähige Komponenten, die konzeptionell aussagekräftig sind und die ich zurücksetzen möchte. Das ideale Verhalten wäre gleichbedeutend mit dem Entfernen der alten Komponente und dem Lesen einer neuen, makellosen Komponente.

React bietet eine Methode setState, mit der der explizite Status der Komponenten festgelegt werden kann, die jedoch implizite Status wie Browserfokus und Formularstatus ausschließt und den Status der untergeordneten Komponenten ausschließt. Es kann eine schwierige Aufgabe sein, all diesen indirekten Zustand zu erfassen, und ich würde es vorziehen, ihn rigoros und vollständig zu lösen, anstatt mit jedem neuen überraschenden Zustand einen Schlag ins Maul zu spielen.

Gibt es eine API oder ein Muster, um dies zu tun?

Bearbeiten: Ich habe ein triviales Beispiel gemacht, das den this.replaceState(this.getInitialState())Ansatz demonstriert und dem this.setState(this.getInitialState())Ansatz gegenüberstellt: jsfiddle - replaceStateist robuster.

Eamon Nerbonne
quelle

Antworten:

206

Um sicherzustellen, dass der von Ihnen erwähnte implizite Browserstatus und der Status der untergeordneten Elemente zurückgesetzt werden, können Sie der von zurückgegebenen Komponente auf Stammebene ein keyAttribut hinzufügen render. Wenn es sich ändert, wird diese Komponente weggeworfen und von Grund auf neu erstellt.

render: function() {
    // ...
    return <div key={uniqueId}>
        {children}
    </div>;
}

Es gibt keine Verknüpfung zum Zurücksetzen des lokalen Status der einzelnen Komponente.

Sophie Alpert
quelle
1
Nur aus Neugier, wenn Sie sagen, dass die Komponente weggeworfen und von Grund auf neu erstellt wird, meinen Sie damit, dass das virtuelle DOM weggeworfen und von Grund auf neu erstellt wird, die DOM-Aktualisierungen jedoch weiterhin auf der Grundlage des Diff-Algorithmus erfolgen?
Nimgrg
1
@nimgrg Nein, die realen DOM-Elemente werden ebenfalls neu erstellt. (Das virtuelle DOM wird bereits bei jedem Rendering neu erstellt. Wenn Sie also das reale DOM behalten möchten, legen Sie keyin diesem Fall einfach kein fest .)
Sophie Alpert
3
@EamonNerbonne Bei React 0.8 werden Schlüssel nur zur Unterscheidung zwischen Geschwisterelementen in einem Array verwendet und im Stammverzeichnis ignoriert. Siehe github.com/facebook/react/issues/590 .
Sophie Alpert
Hinweis replaceState()und getInitialState()werden in den neuen ES6 Klasse-Stil reactjs beide veraltet. Verwenden Sie this.setState(this._getInitialState())stattdessen. Außerdem können Sie Ihre eigene Statusinitialisierungsfunktion nicht benennen getInitialState()- reagieren löst eine Warnung aus - rufen Sie etwas anderes auf und tun dies explizit state = this._getInitialState()in der obersten Ebene der Klasse.
thom_nic
2
Gibt es eine Möglichkeit, dies von der Innenseite einer Komponente aus zu tun, ohne dass die Außenseite eine Schlüsselstütze passieren muss?
Trusktr
14

Sie sollten tatsächlich vermeiden replaceStateund setStatestattdessen verwenden.

Die Dokumente sagen, dass replaceState"in einer zukünftigen Version von React möglicherweise vollständig entfernt wird". Ich denke, es wird definitiv entfernt, weil replaceStatees nicht wirklich mit der Philosophie von React übereinstimmt. Es erleichtert es, dass sich eine React-Komponente wie ein Schweizer Messer anfühlt. Dies wirkt dem natürlichen Wachstum einer React-Komponente entgegen, die kleiner und zweckmäßiger wird.

Wenn Sie in React auf Generalisierung oder Spezialisierung verzichten müssen, streben Sie eine Spezialisierung an. Als Konsequenz sollte der Statusbaum für Ihre Komponente eine gewisse Sparsamkeit aufweisen (es ist in Ordnung, diese Regel geschmackvoll zu brechen, wenn Sie ein brandneues Produkt auf den Markt bringen).

Wie auch immer, so machst du es. Ähnlich wie Bens (akzeptierte) Antwort oben, aber so:

this.setState(this.getInitialState());

Außerdem müssen Sie (wie auch Ben sagte) diesen DOM-Knoten entfernen, um den "Browser-Status" zurückzusetzen. Nutzen Sie die Kraft des Reiches und verwenden Sie eine neue keyRequisite für diese Komponente. Das neue Rendering ersetzt diese Komponente im Großhandel.

Referenz: https://facebook.github.io/react/docs/component-api.html#replacestate

wle8300
quelle
2
Dies funktioniert nicht, wenn der aktuelle Status Eigenschaften enthält, die sich nicht im Ausgangsstatus befinden. Ich glaube zwar nicht, dass dies ein großartiger "Zustand" ist, aber wenn Sie es nicht irgendwie verhindern können, möchte ich überraschende Brüche vermeiden. Ich wäre mit einem Reset einverstanden, der in einigen Fällen abstürzt , aber nicht mit einem Reset, der den Status auf subtile Weise beschädigt.
Eamon Nerbonne
Ich bin mir nicht sicher, ob ich folge. Wollen Sie damit sagen, dass dies nicht gleichbedeutend ist mit this.replaceState? Weil es so ist.
wle8300
1
@ williamle8300 Sie wären nicht gleichwertig, wenn eine neue Eigenschaft hinzugefügt würde, um irgendwo im Komponentenlebenszyklus zwischen Aufrufen von getInitialState()(z. B. in einem onClick-Handler) anzugeben. In diesem Fall wäre diese neue Immobilie nach dem 2. noch vorhanden getInitialState().
Graham
@ Abraham Ich glaube, es ist eine schlechte Praxis, einer Komponente, die noch nicht deklariert ist, einen neuen Status hinzuzufügen getInitialState. Ähnlich wie beim Deklarieren aller Ihrer Variablen in einer JS-Funktion oben in der Funktion. Randnotiz: Ben Alperts Lösung ist nahezu identisch mit meiner ... und er ist ein offizieller React-Betreuer!
wle8300
1
Ich fürchte, die Funktion getInitialState () und damit die Antwort ist veraltet. Ein Update wäre schön.
Martin R.
8

Wenn keySie dem Element, das Sie neu initialisieren müssen, ein Attribut hinzufügen, wird es jedes Mal neu geladen, wenn sich das Element ändert propsoder statedem Element zugeordnet wird.

key = {new Date (). getTime ()}

Hier ist ein Beispiel:

render() {
  const items = (this.props.resources) || [];
  const totalNumberOfItems = (this.props.resources.noOfItems) || 0;

  return (
    <div className="items-container">
      <PaginationContainer
        key={new Date().getTime()}
        totalNumberOfItems={totalNumberOfItems}
        items={items}
        onPageChange={this.onPageChange}
      />
    </div>
  );
}
jsbisht
quelle
1
Oder Sie könnten eine einfache Ganzzahlzählervariable als Schlüssel hinzufügen
minimalistisch