Reagieren: Warum die untergeordnete Komponente nicht aktualisiert wird, wenn sich die Requisite ändert

132

Warum wird Child im folgenden Pseudocode-Beispiel nicht neu gerendert, wenn der Container foo.bar ändert?

Container {
  handleEvent() {
    this.props.foo.bar = 123
  },

  render() {
    return <Child bar={this.props.foo.bar} />
}

Child {
  render() {
    return <div>{this.props.bar}</div>
  }
}

Auch wenn ich forceUpdate()nach dem Ändern des Werts in Container aufrufe, zeigt Child immer noch den alten Wert an.

Tuomas Toivonen
quelle
5
Ist das dein Code? Scheint, als wäre es kein gültiger
Ich denke, der Wert der Requisiten sollte sich nicht in der Containerkomponente ändern, sondern in der übergeordneten Komponente durch setState, und dieser Status sollte den Requisiten des Containers zugeordnet werden
Piyush Patel
Verwenden Sie den Spread-Operator wie folgt: <Child bar = {... this.props.foo.bar} />
Sourav Singh
@AdrianWydmanski und die anderen 5 Personen, die positiv bewertet haben: en.wikipedia.org/wiki/Pseudocode
David Newcomb
@ PiyushPatel-Requisiten werden aktualisiert, wenn eine Komponente an Ort und Stelle neu gerendert wird, wie das Beispiel für Pseudocode zeigt. Ein weiteres Beispiel hierfür ist die Verwendung <Route exact path="/user/:email" component={ListUserMessagePage} />. Ein Link auf derselben Seite aktualisiert die Requisiten, ohne eine neue Instanz zu erstellen und die üblichen Lebenszyklusereignisse auszuführen.
David Newcomb

Antworten:

94

Aktualisieren Sie das untergeordnete Element so, dass das Attribut 'Schlüssel' dem Namen entspricht. Die Komponente wird jedes Mal neu gerendert, wenn sich der Schlüssel ändert.

Child {
  render() {
    return <div key={this.props.bar}>{this.props.bar}</div>
  }
}
Polina-c
quelle
5
Danke dafür. Ich habe den Redux-Speicher verwendet und konnte meine Komponente erst dann wieder rendern, wenn ich einen Schlüssel hinzugefügt habe. (Ich habe die UUID-Bibliothek verwendet, um einen zufälligen Schlüssel zu generieren, und es hat funktioniert).
Maiya
Bei Verwendung von Hooks würde mein Kind beim Festlegen des Status für ein vorhandenes Array nicht erneut rendern. Mein (wahrscheinlich schlechter) Hack bestand darin, gleichzeitig den Status einer Dummy-Requisite festzulegen und diesen dem useEffect-Abhängigkeitsarray hinzuzufügen : [props.notRenderingArr, props.dummy]. Wenn sich die Länge des Arrays immer ändert, können Sie das Abhängigkeitsarray useEffect auf setzen [props.notRenderingArr.length].
Hipnosis
5
das war es, was ich auch brauchte. Ich bin verwirrt, wann ist ein keyerforderlich, gegen nur setState? Ich habe einige Kinder, die sich auf den Zustand der Eltern verlassen, aber sie lösen kein erneutes Rendern aus ...
dcsan
Gute Antwort. Aber warum dieses Verhalten?
Rishav
1
Ich habe den Index als Schlüssel verwendet, aber er funktionierte nicht richtig. Jetzt benutze ich uuid und es ist perfekt :) Danke an @Maiya
Ali Rehman
89

Weil Kinder nicht erneut rendern, wenn sich die Requisiten des Elternteils ändern, sondern wenn sich der STAAT ändert :)

Was Sie zeigen, ist Folgendes: https://facebook.github.io/react/tips/communicate-between-components.html

Es werden Daten von Eltern zu Kindern über Requisiten weitergegeben, aber es gibt dort keine Renderlogik.

Sie müssen einen Status für das übergeordnete Element festlegen und dann das untergeordnete Element beim Ändern des übergeordneten Status erneut rendern. Das könnte helfen. https://facebook.github.io/react/tips/expose-component-functions.html

François Richard
quelle
5
Warum bewirkt setState ein erneutes Rendern, ForceUpdate jedoch nicht? Auch wenig abseits des Themas, aber wie werden die Komponenten aktualisiert, wenn Requisiten von Redux übergeben werden und der Status durch Aktion aktualisiert wird?
Tuomas Toivonen
Requisiten werden nicht aktualisiert, auch wenn Sie die Komponente aktualisieren. Die von Ihnen übergebenen Requisiten sind noch vorhanden. Flux ist ein anderes Thema ja :)
François Richard
3
state! = Requisiten, über die Sie mehr lesen müssen. Sie machen keine Updates mit Requisiten
François Richard
Sie können es direkt im Quellcode sehen, es ist einfach nicht der gleiche Prozess
Webwoman
Also, was Sie hier gesagt haben, wurde entweder geändert oder ich habe es nicht richtig verstanden. Ich habe übrigens eine Frage dazu gestellt: stackoverflow.com/questions/58903921/…
ILoveReactAndNode
49

Ich hatte das gleiche Problem. Dies ist meine Lösung. Ich bin mir nicht sicher, ob dies die gute Praxis ist. Sagen Sie mir, wenn nicht:

state = {
  value: this.props.value
};

componentDidUpdate(prevProps) {
  if(prevProps.value !== this.props.value) {
    this.setState({value: this.props.value});
  }
}

UPD: Jetzt können Sie dasselbe mit React Hooks tun: (nur wenn die Komponente eine Funktion ist)

const [value, setValue] = useState(propName);
// This will launch only if propName value has chaged.
useEffect(() => { setValue(propName) }, [propName]);
Petrichor
quelle
3
Dies ist definitiv die richtige Antwort, ganz zu schweigen von der einfachsten
Guilherme Ferreira
1
Ich benutze auch diesen Ansatz.
roh
@ vancy-pant Das geht componentDidUpdatein diesem Fall in die Child-Komponente.
Nick Friskel
4
Sie brauchen und sollten Requisiten nicht in eine untergeordnete Komponente umwandeln. Verwenden Sie einfach die Requisiten direkt. Darin FunctionComponentswerden untergeordnete Komponenten automatisch aktualisiert, wenn sich die Requisiten ändern.
Oemera
1
Dieser Ansatz funktioniert auch, wenn Sie Requisiten in
untergeordneten
10

Laut React-Philosophie kann die Komponente ihre Requisiten nicht ändern. Sie sollten vom Elternteil erhalten und unveränderlich sein. Nur Eltern können die Requisiten ihrer Kinder ändern.

nette Erklärung zu Zustand gegen Requisiten

Lesen Sie auch diesen Thread. Warum kann ich Requisiten in react.js nicht aktualisieren?

Sujan Thakare
quelle
Bedeutet dies nicht auch, dass der Container die Requisiten, die er vom Redux Store erhält, nicht ändern kann?
Tuomas Toivonen
3
Container erhält die Requisiten nicht direkt aus dem Laden, sondern wird durch Lesen eines Teils eines Redux-Statusbaums geliefert (verwirrend ??). !! Verwirrung ist, weil wir nicht wissen, welche magische Funktion durch React-Redux verbunden ist. Wenn Sie den Quellcode der Verbindungsmethode sehen, wird im Grunde eine Komponente höherer Ordnung erstellt, die einen Status mit der Eigenschaft storeState hat und den Redux-Speicher abonniert hat. Wenn sich storeState ändert, erfolgt das gesamte Rendern, und die geänderten Requisiten werden durch Lesen des geänderten Status an den Container geliefert. hoffe, dies beantwortet die Frage
Sujan Thakare
Gute Erklärung, wenn ich über eine Komponente höherer Ordnung nachdenke, kann ich besser verstehen, was passiert
Tuomas Toivonen,
7

Sie sollten die setStateFunktion verwenden. Wenn nicht, speichert state Ihre Änderung nicht, unabhängig davon, wie Sie forceUpdate verwenden.

Container {
    handleEvent= () => { // use arrow function
        //this.props.foo.bar = 123
        //You should use setState to set value like this:
        this.setState({foo: {bar: 123}});
    };

    render() {
        return <Child bar={this.state.foo.bar} />
    }
    Child {
        render() {
            return <div>{this.props.bar}</div>
        }
    }
}

Ihr Code scheint ungültig zu sein. Ich kann diesen Code nicht testen.

JamesYin
quelle
Sollte es nicht sein <Child bar={this.state.foo.bar} />?
Josh Broadhurst
Richtig. Ich aktualisiere die Antwort. Aber wie gesagt, dies ist möglicherweise kein gültiger Code. Einfach von der Frage kopieren.
JamesYin
5

Beim Erstellen Reagieren Sie Komponenten aus functionsund useState.

const [drawerState, setDrawerState] = useState(false);

const toggleDrawer = () => {
      // attempting to trigger re-render
      setDrawerState(!drawerState);
};

Dies gilt nicht Arbeit

         <Drawer
            drawerState={drawerState}
            toggleDrawer={toggleDrawer}
         />

Dies funktioniert (Hinzufügen eines Schlüssels)

         <Drawer
            drawerState={drawerState}
            key={drawerState}
            toggleDrawer={toggleDrawer}
         />
Nelles
quelle
3

Bestätigt, das Hinzufügen eines Schlüssels funktioniert. Ich ging die Dokumente durch, um zu verstehen, warum.

React möchte beim Erstellen untergeordneter Komponenten effizient sein. Eine neue Komponente wird nicht gerendert, wenn sie mit einem anderen untergeordneten Element identisch ist, wodurch die Seite schneller geladen wird.

Durch Hinzufügen eines Schlüssels wird die Reaktion zum Rendern einer neuen Komponente erzwungen, wodurch der Status für diese neue Komponente zurückgesetzt wird.

https://reactjs.org/docs/reconciliation.html#recursing-on-children

tjr226
quelle
2

Verwenden Sie die Funktion setState. Also könntest du es tun

       this.setState({this.state.foo.bar:123}) 

innerhalb der Handle-Event-Methode.

Sobald der Status aktualisiert wurde, werden Änderungen ausgelöst und erneut gerendert.

Indraneel Bende
quelle
1
Es sollte this.setState ({foo.bar:123}) sein, oder?
Charith Jayasanka
0

Sie sollten das untergeordnete Element wahrscheinlich als Funktionskomponente festlegen, wenn es keinen Status beibehält und die Requisiten einfach rendert und dann vom übergeordneten Element aus aufruft. Alternativ dazu können Sie Hooks mit der Funktionskomponente (useState) verwenden, wodurch die zustandslose Komponente erneut gerendert wird.

Sie sollten die Propas auch nicht ändern, da sie unveränderlich sind. Behalten Sie den Status der Komponente bei.

Child = ({bar}) => (bar);
Satyam Soni
quelle
0

Ich hatte das gleiche Problem. Ich hatte eine TooltipKomponente, die eine showTooltipRequisite erhielt, die ich Parentbasierend auf einer ifBedingung für eine ParentKomponente aktualisierte. Sie wurde in der Komponente aktualisiert, aber die TooltipKomponente wurde nicht gerendert.

const Parent = () => {
   let showTooltip = false;
   if(....){ showTooltip = true; }
   return(
      <Tooltip showTooltip={showTooltip}></Tooltip>
   )
}

Der Fehler, den ich gemacht habe, ist, showTooltipals vermietet zu deklarieren .

Mir wurde klar, was ich falsch gemacht habe. Ich habe gegen die Prinzipien des Renderns verstoßen. Das Ersetzen durch Hooks hat den Job gemacht.

const [showTooltip, setShowTooltip] =  React.useState<boolean>(false);
Divyanshu Rawat
quelle
0

Definieren Sie geänderte Requisiten in mapStateToProps der Verbindungsmethode in der untergeordneten Komponente.

function mapStateToProps(state) {
  return {
    chanelList: state.messaging.chanelList,
  };
}

export default connect(mapStateToProps)(ChannelItem);

In meinem Fall wird der Kanal von channelList aktualisiert, sodass ich chanelList in mapStateToProps hinzugefügt habe

Rajesh Nasit
quelle