Aktualisieren des Status bei Änderung der Requisiten in der Reaktionsform

183

Ich habe Probleme mit einem Reaktionsformular und der ordnungsgemäßen Verwaltung des Status. Ich habe ein Zeit-Eingabefeld in einem Formular (in einem Modal). Der Anfangswert wird als Statusvariable in festgelegt getInitialStateund von einer übergeordneten Komponente übergeben. Dies an sich funktioniert gut.

Das Problem tritt auf, wenn ich den Standardwert start_time über die übergeordnete Komponente aktualisieren möchte. Das Update selbst erfolgt in der übergeordneten Komponente durch setState start_time: new_time. In meinem Formular ändert sich der Standardwert start_time jedoch nie, da er nur einmal in definiert wird getInitialState.

Ich habe versucht, componentWillUpdateeine Zustandsänderung durchzusetzen setState start_time: next_props.start_time, die tatsächlich funktioniert hat, aber mir Uncaught RangeError: Maximum call stack size exceededFehler gegeben hat.

Meine Frage ist also, wie man den Status in diesem Fall richtig aktualisiert. Denke ich irgendwie falsch darüber nach?

Aktueller Code:

@ModalBody = React.createClass
  getInitialState: ->
    start_time: @props.start_time.format("HH:mm")

  #works but takes long and causes:
  #"Uncaught RangeError: Maximum call stack size exceeded"
  componentWillUpdate: (next_props, next_state) ->
    @setState(start_time: next_props.start_time.format("HH:mm"))

  fieldChanged: (fieldName, event) ->
    stateUpdate = {}
    stateUpdate[fieldName] = event.target.value
    @setState(stateUpdate)

  render: ->
    React.DOM.div
      className: "modal-body"
      React.DOM.form null,
        React.createElement FormLabelInputField,
          type: "time"
          id: "start_time"
          label_name: "Start Time"
          value: @state.start_time
          onChange: @fieldChanged.bind(null, "start_time”)

@FormLabelInputField = React.createClass
  render: ->
    React.DOM.div
      className: "form-group"
      React.DOM.label
        htmlFor: @props.id
        @props.label_name + ": "
      React.DOM.input
        className: "form-control"
        type: @props.type
        id: @props.id
        value: @props.value
        onChange: @props.onChange
David Basalla
quelle

Antworten:

285

componentWillReceiveProps wird seit Reaktion 16 beschrieben: Verwenden Sie stattdessen getDerivedStateFromProps

Wenn ich das richtig verstehe, haben Sie eine übergeordnete Komponente, start_timedie an die ModalBodyKomponente weitergegeben wird, die sie ihrem eigenen Status zuweist? Und Sie möchten diese Zeit vom übergeordneten Element aus aktualisieren, nicht von einer untergeordneten Komponente.

React hat einige Tipps zum Umgang mit diesem Szenario. (Beachten Sie, dass dies ein alter Artikel ist, der inzwischen aus dem Web entfernt wurde. Hier ist ein Link zum aktuellen Dokument zu Komponenten-Requisiten. )

Die Verwendung von Requisiten zur Erzeugung eines Zustands getInitialStateführt häufig zu einer Verdoppelung der "Quelle der Wahrheit", dh wo sich die realen Daten befinden. Dies liegt daran, dass getInitialStatenur aufgerufen wird, wenn die Komponente zum ersten Mal erstellt wird.

Berechnen Sie nach Möglichkeit die Werte im laufenden Betrieb, um sicherzustellen, dass sie später nicht nicht mehr synchron sind und Wartungsprobleme verursachen.

Grundsätzlich wird die Render-Methode beim Requisiten-Update nicht immer aufgerufen , wenn Sie propseinem Kind Eltern zuweisen state. Sie müssen es manuell aufrufen, indem Sie diecomponentWillReceiveProps Methode .

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}
Brad B.
quelle
83
Veraltet ab Reaktion 16
Typ
7
@dude Es ist noch nicht veraltet. Was Sie meinen, ist nur ein Hinweis auf zukünftige Referenz. Ich zitiere[..]going to be deprecated in the future
paddotk
7
@poepje Es ist vielleicht noch nicht veraltet, aber es wird vom aktuellen Standard als unsicher angesehen und sollte wahrscheinlich vermieden werden
Unflores
12
Was sollte die neue Methode sein, nachdem componentWillReceiveProps veraltet ist?
Boris D. Teoharov
4
@Boris Jetzt sagt dir das Reaktionsteam im Grunde, du sollst vollgestopft werden. Sie geben Ihnen eine neue Methode namens getDerivedStateFromProps. Der Haken ist, dass dies eine statische Methode ist. Das heißt, Sie können nichts Asynchrones tun, um den Status zu aktualisieren (da Sie den neuen Status sofort zurückgeben müssen), und Sie können auch nicht auf Klassenmethoden oder Felder zugreifen. Sie können auch Memoization verwenden, dies passt jedoch nicht zu jedem Anwendungsfall. Wieder einmal möchte das Reaktionsteam seine Arbeitsweise erzwingen. Es ist eine extrem dumme und unfähige Designentscheidung.
Ig-Dev
76

Anscheinend ändern sich die Dinge ... getDerivedStateFromProps () ist jetzt die bevorzugte Funktion.

class Component extends React.Component {
  static getDerivedStateFromProps(props, current_state) {
    if (current_state.value !== props.value) {
      return {
        value: props.value,
        computed_prop: heavy_computation(props.value)
      }
    }
    return null
  }
}

(über Code von danburzo @ github)

ErichBSchulz
quelle
7
Zu nullIhrer return null
Information
@ IlgıtYıldırım - Code bearbeitet, seit 4 Personen Ihren Kommentar positiv bewertet haben - macht es wirklich einen Unterschied?
ErichBSchulz
Es gibt eine ziemlich gute Ressource, die sich eingehend mit verschiedenen Optionen befasst und erklärt, warum Sie entweder getDerivedStateFromPropsoder memoization
reactjs.org/blog/2018/06/07/… verwenden
2
getDerivedStateFromProps muss statisch sein. Das heißt, Sie können nichts Asynchrones tun, um den Status zu aktualisieren, und Sie können auch nicht auf Klassenmethoden oder Felder zugreifen. Wieder einmal möchte das Reaktionsteam seine Arbeitsweise erzwingen. Es ist eine extrem dumme und unfähige Designentscheidung.
Ig-Dev
39

componentWillReceiveProps wird veraltet, weil die Verwendung "häufig zu Fehlern und Inkonsistenzen führt".

Wenn sich von außen etwas ändert, sollten Sie die untergeordnete Komponente vollständig mit zurücksetzenkey .

Durch das Bereitstellen einer keyRequisite für die untergeordnete Komponente wird sichergestellt, dass keydiese Komponente immer dann neu gerendert wird , wenn sich der Wert von außen ändert. Z.B,

<EmailInput
  defaultEmail={this.props.user.email}
  key={this.props.user.id}
/>

Zu seiner Leistung:

Während dies langsam klingt, ist der Leistungsunterschied normalerweise unbedeutend. Die Verwendung eines Schlüssels kann sogar schneller sein, wenn die Komponenten über eine umfangreiche Logik verfügen, die bei Aktualisierungen ausgeführt wird, da die Differenzierung für diesen Teilbaum umgangen wird.

Lucia
quelle
1
Der Schlüssel, das Geheimnis! Funktioniert perfekt in Reaktion 16 wie oben erwähnt
Darren Sweeney
Schlüssel funktioniert nicht, wenn es sich um ein Objekt handelt und Sie keine eindeutige Zeichenfolge haben
user3468806
Schlüssel funktioniert für Objekte, ich habe es getan. Natürlich hatte ich eine eindeutige Zeichenfolge für den Schlüssel.
Tsujp
@ user3468806 Wenn es sich nicht um ein komplexes Objekt mit externen Referenzen handelt, können Sie JSON.stringify(myObject)einen eindeutigen Schlüssel von Ihrem Objekt ableiten.
Roy Prins
24

Es ist auch componentDidUpdate verfügbar.

Funktionssignatur:

componentDidUpdate(prevProps, prevState, snapshot)

Nutzen Sie dies als Gelegenheit, um das DOM zu bearbeiten, wenn die Komponente aktualisiert wurde. Wird bei der Initiale nicht aufgerufen render.

Wir sehen, dass Sie wahrscheinlich keinen abgeleiteten Statusartikel benötigen , der Anti-Pattern für componentDidUpdateund beschreibt getDerivedStateFromProps. Ich fand es sehr nützlich.

Arminfro
quelle
Ich benutze componentDidUpdatees am Ende, weil es einfach und für die meisten Fälle besser geeignet ist.
KeitelDOG
14

Die neue Methode für Hooks besteht darin, useEffect anstelle von componentWillReceiveProps auf die alte Weise zu verwenden:

componentWillReceiveProps(nextProps) {
  // You don't have to do this check first, but it can help prevent an unneeded render
  if (nextProps.startTime !== this.state.startTime) {
    this.setState({ startTime: nextProps.startTime });
  }
}

wird in einer funktionalen, von Haken angetriebenen Komponente wie folgt:

// store the startTime prop in local state
const [startTime, setStartTime] = useState(props.startTime)
// 
useEffect(() => {
  if (props.startTime !== startTime) {
    setStartTime(props.startTime);
  }
}, [props.startTime]);

Wir setzen den Status mit setState, verwenden useEffect, suchen nach Änderungen an der angegebenen Requisite und ergreifen die Aktion, um den Status bei Änderung der Requisite zu aktualisieren.

MMO
quelle
5

Sie brauchen wahrscheinlich keinen abgeleiteten Zustand

1. Legen Sie einen Schlüssel vom übergeordneten Element fest

Wenn sich ein Schlüssel ändert, erstellt React eine neue Komponenteninstanz, anstatt die aktuelle zu aktualisieren. Schlüssel werden normalerweise für dynamische Listen verwendet, sind aber auch hier nützlich.

2. Verwenden Sie getDerivedStateFromProps/componentWillReceiveProps

Wenn der Schlüssel aus irgendeinem Grund nicht funktioniert (die Initialisierung der Komponente ist möglicherweise sehr teuer)

Durch die Verwendung getDerivedStateFromPropsSie einen Teil Zustand zurückgesetzt, aber es scheint ein wenig Buggy zu diesem Zeitpunkt (v16.7) !, siehe den Link oben für die Nutzung

Ghominejad
quelle
2

Aus der Reaktionsdokumentation: https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html

Das Löschen des Status beim Ändern der Requisiten ist ein Anti-Pattern

Seit Reaktion 16 ist componentWillReceiveProps veraltet. Aus der Reaktionsdokumentation wird in diesem Fall der empfohlene Ansatz verwendet

  1. Voll kontrollierte Komponente: ParentComponentDer ModalBodyWille besitzt den start_timeStaat. Dies ist in diesem Fall nicht mein bevorzugter Ansatz, da ich denke, dass das Modal diesen Zustand besitzen sollte.
  2. Völlig unkontrollierte Komponente mit einem Schlüssel: Dies ist mein bevorzugter Ansatz. Ein Beispiel aus der Reaktionsdokumentation: https://codesandbox.io/s/6v1znlxyxn . Sie würden den start_timeStaat vollständig von Ihrem besitzen ModalBodyund verwenden, getInitialStatewie Sie es bereits getan haben. Um den start_timeStatus zurückzusetzen, ändern Sie einfach den Schlüssel von derParentComponent
Lu Tran
quelle
0

Verwenden Sie Memoize

Die Zustandsableitung des Op ist eine direkte Manipulation von Requisiten, ohne dass eine echte Ableitung erforderlich ist. Mit anderen Worten, wenn Sie eine Requisite haben, die direkt verwendet oder transformiert werden kann, müssen Sie die Requisite nicht im Status speichern .

Da der Statuswert von start_timeeinfach die Requisite ist start_time.format("HH:mm"), reichen die in der Requisite enthaltenen Informationen bereits aus, um die Komponente zu aktualisieren.

Wenn Sie jedoch das Format nur bei einem Requisitenwechsel aufrufen möchten, können Sie dies gemäß der neuesten Dokumentation über Memoize tun: https://reactjs.org/blog/2018/06/07/you-probably-dont- Need-Derivated-State.html # Was ist mit Memoisierung?

DannyMoshe
quelle
-1

Ich denke, Verwendung ref ist sicher für mich, brauchen keine Pflege über eine der oben genannten Methoden.

class Company extends XComponent {
    constructor(props) {
        super(props);
        this.data = {};
    }
    fetchData(data) {
        this.resetState(data);
    }
    render() {
        return (
            <Input ref={c => this.data['name'] = c} type="text" className="form-control" />
        );
    }
}
class XComponent extends Component {
    resetState(obj) {
        for (var property in obj) {
            if (obj.hasOwnProperty(property) && typeof this.data[property] !== 'undefined') {
                if ( obj[property] !== this.data[property].state.value )
                    this.data[property].setState({value: obj[property]});
                else continue;
            }
            continue;
        }
    }
}
Mai Wetter VN
quelle
Ich denke, diese Antwort ist kryptisch (Code ist kaum lesbar und ohne Erklärung / Verknüpfung mit dem Problem von OP) und geht nicht auf das Problem von OP ein, wie man mit dem Anfangszustand umgeht.
Netchkin