So synchronisieren Sie Requisiten mithilfe von React-Hooks zum Status: setState ()

77

Ich versuche, den Status mithilfe von React hook setState () mithilfe der Requisiten festzulegen, die die Komponente erhält. Ich habe versucht, den folgenden Code zu verwenden:

import React,{useState , useEffect} from 'react';

const Persons = (props) =>  {

    // console.log(props.name);

   const [nameState , setNameState] = useState(props)

   console.log(nameState.name);
   console.log(props.name);

   return (
            <div>
                <p>My name is {props.name} and my age is {props.age}</p>
                <p>My profession is {props.profession}</p>
            </div>
        )

}

export default Persons;

Das Problem ist, dass der Status beim Laden der Komponente festgelegt wird. Aber wenn es neue Requisiten erhält, wird der Zustand nicht aktualisiert. Wie aktualisiere ich den Status in diesem Fall? Danke im Voraus.

METALLKOPF
quelle
Können Sie den Code in hinzufügen setNameState.
ManavM
Hallo Manav, können wir Requisiten nicht mit useState synchronisieren, wenn die Requisiten jedes Mal aktualisiert werden?
METALHEAD
Nein, diese Zeile initialisiert nur Ihren Status. Wenn Sie Ihren Status jedes Mal mit Requisiten aktualisieren möchten, müssen Sie so etwas wieuseEffect
ManavM

Antworten:

110

useStateDas Hooks-Funktionsargument wird nur einmal verwendet und nicht jedes Mal, wenn sich die Requisite ändert. Sie müssen useEffectHooks verwenden, um das zu implementieren, was Sie als componentWillReceiveProps/getDerivedStateFromPropsFunktionalität bezeichnen würden

import React,{useState , useEffect} from 'react';

const Persons = (props) =>  {
   const [nameState , setNameState] = useState(props)

   useEffect(() => {
       setNameState(props);
   }, [props])

   return (
            <div>
                <p>My name is {props.name} and my age is {props.age}</p>
                <p>My profession is {props.profession}</p>
            </div>
        )

}

export default Persons;
Shubham Khatri
quelle
2
Es gab Diskussionen darüber, wie der Status von Requisiten in einem Anti-Muster initialisiert werden kann, aber da wir Haken haben, denke ich, dass sich die Dinge geändert haben. Aber ich bin mir nicht sicher. Was denken Sie?
Imdadul Huq Naim
9
Dadurch wird die Komponente jedoch wieder richtig dargestellt. Dies war in getDerivedStateFromProps nicht der Fall, wenn wir die Requisiten mit dem Status synchronisieren.
METALHEAD
8
Warum verwenden Sie props.nameund props.ageetc. in der Rücksendung anstelle von nameState.name& nameState.ageetc.?
Sutherlandahoy
7
Dieser Ansatz sieht gut aus, aber wird diese Komponente nicht zweimal gerendert? Weil wir setState () in useEffect verwenden. Schlägt dies nicht die Leistung?
METALHEAD
6
Falsche Antwort, dies führt zu einer verschwenderischen Geisterwiedergabe. useEffect hat hier kein Geschäft. Der einfachste Weg, dies zu tun, ist: reactjs.org/docs/… In einigen Fällen sind fortgeschrittenere Techniken anwendbar, z. B. das Codieren eines useComputedState-Hooks. Einige andere Vorschläge: reactjs.org/blog/2018/06/07/… Dies ist ein unangenehmes Nebenprodukt von React Hooks.
AlexG
12

Diese allgemeine Idee kann in den Haken gelegt werden:

export function useStateFromProp(initialValue) {
  const [value, setValue] = useState(initialValue);

  useEffect(() => setValue(initialValue), [initialValue]);

  return [value, setValue];
}


function MyComponent({ value: initialValue }) {
  const [value, setValue] = useStateFromProp(initialValue);

  return (...);
}
bsapaka
quelle
11

Der propsWert in useState(props)wird nur während des ersten Renderns verwendet , weitere Statusaktualisierungen werden mit dem Setter durchgeführt setNameState.

Darüber hinaus ist useEffectbeim Aktualisieren des abgeleiteten Status Folgendes nicht erforderlich :

const Person = props => {
  const [nameState, setNameState] = useState(props.name);
  // update derived state conditionally without useEffect
  if (props.name !== nameState) setNameState(props.name);
  // ... other render code
};

Aus React-Dokumenten :

[...] Sie können den Status direkt während des Renderns aktualisieren . Reagieren Sie werden erneut ausführen um die Komponente mit aktualisierten Zustand unmittelbar nach dem Verlassen des ersten machen , so dass es nicht teuer wäre.

[...] ein Update beim Rendern ist genau das, was getDerivedStateFromPropskonzeptionell immer so war.

Im Wesentlichen können wir die Leistung optimieren, indem wir eine zusätzliche Browser-Repaint-Phase wie z useEffect immer ausgeführt wird, nachdem das Rendern auf dem Bildschirm festgeschrieben wurde.

Arbeitsbeispiel

Dies ist ein erfundenes Beispiel, das das obige Muster veranschaulicht - in echtem Code würden Sie props.namedirekt lesen . Weitere geeignete Anwendungsfälle für abgeleitete Zustände finden Sie im React-Blogbeitrag .

ford04
quelle
Aber gilt das, wenn wir ein ganz neues Objekt als Requisite bekommen und es mit dem Staat synchronisieren müssen? Wie können wir die tiefe Suche in einem Objekt durchführen, um zu überprüfen, was sich geändert hat und was nicht?
METALHEAD
1
Nicht sicher, was Sie versuchen zu tun. Obwohl Sie die tiefe Vergleichslogik manuell schreiben müssten, wie im obigen Code : if (props.name.first !== nameState.first). Oder verwenden Sie einen tiefen Vergleichshelfer
ford04
6

Dafür müssen Sie das verwenden, useEffectdamit Ihr Code aussieht. Da Sie vermeiden möchten, erneut zu rendern, wenn sich die Profis nicht geändert haben, müssen Sie zuerst useEffect überprüfen und dann die Requisiten auf die aktuelle Variable setzen.

import React, { useState, useEffect } from "react";

const Persons = props => {
  // console.log(props.name);

  const [nameState, setNameState] = useState(props);

  console.log(nameState.name);
  console.log(props.name);
  useEffect(
    () => {
      if (nameState !== props.name) {
        setNameState(props.name);
      }
    },
    [nameState]
  );
  return (
    <div>
      <p>
        My name is {props.name} and my age is {props.age}
      </p>
      <p>My profession is {props.profession}</p>
    </div>
  );
};

export default Persons;

Demo

Dhaval Patel
quelle
1
Dieser Ansatz sieht gut aus, aber wird diese Komponente nicht zweimal gerendert? Weil wir setState () in useEffect verwenden. Schlägt dies nicht die Leistung?
METALHEAD
1
@METALHEAD: Es wird nicht zweimal aufgerufen, da wir auch überprüft haben, ob nameState geändert wurde, dann sollte nur useEffect ausgeführt werden, siehe das zweite Argument von useEffect
Dhaval Patel
3
@DhavalPatel Die geänderte Requisite hat ein Rendering ausgelöst und das setNameState()löst ein anderes aus. Scheint mir zweimal.
Drazen Bjelovuk
1
import React, { useState, useEffect } from "react";

const Persons = props => {
  // console.log(props.name);

  const [nameState, setNameState] = useState(props);

  console.log(nameState.name);
  console.log(props.name);
  useEffect(
    () => {
      if (nameState !== props) {
        setNameState(props);
      }
    },
    [nameState]
  );
  return (
    <div>
      <p>
        My name is {props.name} and my age is {props.age}
      </p>
      <p>My profession is {props.profession}</p>
    </div>
  );
};

export default Persons;

Gemäß dem Hooks-Reaktionsdokument wird useEffect immer dann aufgerufen, wenn Requisiten oder Komponenten aktualisiert werden. Sie müssen also die Bedingung überprüfen, bevor Sie useState aktualisieren, und dann Ihren Wert aktualisieren, damit er nicht ständig neu gerendert wird

Kajol Chaudhary
quelle
0

Ich glaube, das Problem deutet auf einen Versuch hin, eine konzeptionelle Variable oder einen Satz von Variablen zu verwenden, um zwei verschiedene Dinge zu tun. Zum Beispiel versuchen, dasselbe zu bekommen props.nameund namezu tun.

Also wenn

const [name, setName] = useState(props.name)

ist nicht genug und Sie versuchen, später in der Funktion in die props.nameStatusvariable zu zwingen, namedann namewird sie möglicherweise überlastet. Versuchen Sie, eine andere Zustandsvariable einzurichten - z.updatedNameund sehen, ob die Dinge besser funktionieren.

Das ursprüngliche Beispiel zeigt dieses Problem nicht, da die Statusvariablen nur in Protokollanweisungen verwendet werden.

Wenn es const [name, setName] = useState(props.name)bei einem erneuten Rendern aktualisiert wird, macht es keinen Sinn, eine Statusvariable zu haben, nameda diese immer dieselbe ist wie props.name(und weitere Versuche, sie zu ändern, würden ein erneutes Rendern verursachen).

Ross Attrill
quelle