State nach oben verlagern heißt, State aus den Komponenten, die ihn teilen müssen, in ihr nächstes gemeinsames Elternteil zu verschieben. Das Elternteil hält den Wert, reicht ihn als Prop nach unten und übergibt eine Funktion, die die Kinder aufrufen, um ihn zu ändern, also rendert jedes Kind aus einer Kopie des States.
Klicke bei einem beliebigen Panel auf „Show“: Es öffnet sich, und das bisher offene schließt sich. Es kann immer nur ein Panel offen sein, weil die Panels das nicht selbst entscheiden. App hält openIndex, und jedes Panel bekommt nur isOpen und eine Funktion onOpen.
Das Problem: State, der übereinstimmen soll
Beginne mit der naheliegenden Version, in der jedes Panel seinen eigenen State isOpen besitzt. Jedes Panel funktioniert, aber die Panels wissen nichts voneinander, also hindert nichts zwei von ihnen daran, gleichzeitig offen zu sein.
Öffne beide Panels: Sie bleiben zusammen offen. State ist in React privat für die Komponente, die ihn deklariert, also kann ein Geschwister ihn weder lesen noch zurücksetzen. Wenn zwei Komponenten übereinstimmen müssen, muss der State über beiden leben.
State in drei Schritten nach oben verlagern
Aus dem zweiten Beispiel das erste zu machen braucht drei Änderungen.
- Entferne den State aus dem Kind. Lösch
useStateinPanelund liesisOpenstattdessen aus den Props. Das Kind entscheidet nicht mehr, ob es offen ist. - Übergib vom Elternteil den Wert und einen Weg, ihn zu ändern.
PanelbekommtisOpenund einen CallbackonOpen. Das Kind ruftonOpen()auf, wenn sein Button geklickt wird; was das Elternteil damit macht, weiß es nicht. - Füge den State dem gemeinsamen Elternteil hinzu.
AppdeklariertopenIndexund macht daraus Props für jedes Panel:isOpen={openIndex === 1}undonOpen={() => setOpenIndex(1)}.
Das nächste gemeinsame Elternteil ist die tiefste Komponente, die alle Komponenten rendert, die den State brauchen. Hier ist das App. Säßen die Panels in einer Komponente Faq, käme der State in Faq, nicht höher.
Nach dem Verlagern wird Panel von seinem Elternteil kontrolliert, im selben Sinn wie ein kontrolliertes Input: Es zeigt, was die Props sagen, und meldet Änderungen über einen Callback. Die Seite zu kontrollierten vs. unkontrollierten Komponenten behandelt dieselbe Idee für Formularelemente.
Eine einzige Quelle der Wahrheit
Wenn zwei Teile des Bildschirms dieselbe Tatsache zeigen, speichere diese Tatsache einmal und berechne alles andere daraus. Ein Temperaturumrechner ist der klassische Fall: Die Inputs für Celsius und Fahrenheit müssen immer übereinstimmen, also können sie nicht jeweils ihre eigene Zahl halten.
Tippe in eines der Felder, und das andere folgt. Der State ist eine Tatsache: die zuletzt getippte Zahl und in welcher Skala sie war. Das andere Feld wird beim Rendern daraus berechnet, also behält das Feld, in das du tippst, immer genau das, was du getippt hast. Ändere den Start-State zu { value: '212', scale: 'f' }, und die Meldung unter den Inputs wechselt zu „Water boils.“
Eine Celsius-Zahl und eine Fahrenheit-Zahl in zwei State-Werten zu halten würde bedeuten, dass jeder Handler beide aktualisieren muss, und sobald ein Handler es einmal vergisst, stimmen die beiden Inputs nicht mehr überein. Ein einziger gespeicherter Wert kann sich nicht selbst widersprechen.
Den Setter nach unten reichen
Ein Kind kann den State des Elternteils nur über eine Funktion ändern, die das Elternteil ihm gibt. Du kannst den Setter selbst übergeben (onSelect={setColor}) oder eine Funktion, die mehr tut (onOpen={() => setOpenIndex(1)}). Gibst du der Prop einen Namen im Event-Stil wie onSelect oder onChange statt setSelected, weiß das Kind nicht, wie das Elternteil den Wert speichert, also kann das Elternteil das später ändern, ohne das Kind anzufassen.
ColorPicker und Preview reden nie miteinander. Der Picker meldet eine Auswahl nach oben, App speichert sie, und der neue Wert fließt zu beiden nach unten.
Wann du State nicht verlagern solltest
Verlagern hat seinen Preis. Wenn State in einem Elternteil lebt, rendert jede Änderung das Elternteil und standardmäßig alle seine Kinder, auch die, die den State nicht nutzen. Verlagere nur bis zum nächsten gemeinsamen Elternteil und lass State, den nur eine Komponente nutzt, in dieser Komponente.
Tippe in das Feld und beobachte die Konsole: Nur SearchBox rendert bei jedem Tastendruck. Verschiebe jetzt query nach oben in App und übergib es SearchBox als Props. Dann loggt jeder Tastendruck auch ProductList, obwohl die Liste die Suche nicht nutzt. Würde die Liste nach der Suche filtern, wäre Verlagern die richtige Entscheidung, weil dann beide Komponenten vom selben Wert abhängen.
Die Frage ist: „Wer muss diesen Wert lesen?“ Lautet die Antwort eine Komponente, bleibt der State dort. Sind es mehrere, kommt er in ihr nächstes gemeinsames Elternteil.
Wenn das Verlagern zu weit geht
Manchmal liegt das nächste gemeinsame Elternteil weit oben im Baum, und der Wert muss durch mehrere Komponenten gereicht werden, die ihn nur weitergeben. Das ist Prop Drilling. Ein paar Ebenen mit Props sind in Ordnung und leicht nachzuvollziehen. Wenn derselbe Wert durch viele Ebenen wandert oder fast jede Komponente ihn braucht (der angemeldete Nutzer, das Theme, die Sprache), lies ihn mit useContext, statt ihn von Hand weiterzureichen. Context ändert, wie der Wert die Kinder erreicht; der State selbst lebt weiterhin in einem Elternteil, ist also weiterhin nach oben verlagert.
Häufig gestellte Fragen
Was bedeutet Lifting State Up in React?
Ein Stück State aus den Komponenten, die es nutzen, in ihr nächstes gemeinsames Elternteil zu verschieben. Das Elternteil besitzt den State und reicht den Wert sowie eine Funktion zum Ändern als Props an die Kinder weiter.
Wie teilen sich zwei Geschwisterkomponenten in React State?
Geschwister können den State des anderen nicht lesen. Lege den State in ihr gemeinsames Elternteil, übergib den Wert an beide und einen Setter (oder einen Handler wie onChange) an das, das ihn ändert. Dann rendern beide Geschwister aus demselben Wert.
Wie aktualisiert eine Kindkomponente den State des Elternteils?
Das Elternteil übergibt eine Funktion als Prop, zum Beispiel onSelect={setSelected} oder onSelect={(id) => setSelected(id)}, und das Kind ruft sie auf. Der State bleibt im Elternteil; das Kind bittet nur um die Änderung.
Wann sollte ich State nicht nach oben verlagern?
Wenn nur eine Komponente den State nutzt. Ihn höher als nötig zu verlagern lässt das Elternteil bei jeder Änderung rendern und verteilt Props durch Komponenten, die sie nicht interessieren. Halte State so nah wie möglich an der Stelle, wo er gebraucht wird.
Was ist die Alternative, wenn State zu weit nach oben wandert?
Wenn du dieselben Props durch viele Ebenen reichst, lies den gemeinsamen Wert stattdessen mit Context (useContext) oder strukturiere um, damit die Komponenten, die ihn brauchen, näher beieinander liegen.