Ist setState synchron oder asynchron? Asynchrones Aktualisieren des Status in benutzerdefinierten synthetischen Ereignissen und React-Hook-FunktionenNehmen Sie setState in einem benutzerdefinierten Klickereignis als Beispiel importiere React, {Komponente} von „react“; Klasse Test erweitert Komponente { Konstruktor(Requisiten) { super(Requisiten); dieser.Zustand = { Anzahl: 1 }; } handleKlick = () => { dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }); dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }); dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }); Konsole.log(dieser.Zustand.Anzahl); } rendern() { zurückkehren ( <div Stil = {{ Breite: '100px', Höhe: '100px', Hintergrundfarbe: "gelb" }}> {dieser.Zustand.Anzahl} </div> ) } } Standardtest exportieren; Klicken Sie einmal, und das endgültige gedruckte Ergebnis von this.state.count ist 1, und auf der Seite wird 2 angezeigt. Aus dem Phänomen ist ersichtlich, dass nur der letzte der drei SetStates wirksam ist und die ersten beiden SetStates wirkungslos sind. Denn wenn der erste SetState auf +3 geändert wird, ist das Zähldruckergebnis 1 und das Anzeigeergebnis 2, und es gibt keine Änderung. Und es erfolgt keine Synchronisation um das Zählergebnis zu erhalten. An diesem Punkt können wir den Code anpassen, um den aktualisierten Status über den zweiten Parameter von setState abzurufen: importiere React, {Komponente} von „react“; Klasse Test erweitert Komponente { Konstruktor(Requisiten) { super(Requisiten); dieser.Zustand = { Anzahl: 1 }; } handleKlick = () => { dies.setState({ Anzahl: dieser.Zustand.Anzahl + 3 }, () => { Konsole.log('1', dieser.Status.Anzahl) }); dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }, () => { Konsole.log('2', dieser.Zustand.Anzahl); }); dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }, () => { Konsole.log('3', dieser.Zustand.Anzahl); }); Konsole.log(dieser.Zustand.Anzahl); } rendern() { zurückkehren ( <div Stil = {{ Breite: '100px', Höhe: '100px', Hintergrundfarbe: "gelb" }}> {dieser.Zustand.Anzahl} </div> ) } } Standardtest exportieren; Klicken Sie jetzt einmal, und die Druckergebnisse der drei setState-Rückruffunktionen sind wie folgt.
Erstens gibt die letzte Zeile direkt 1 aus. Dann werden im Rückruf von setState alle zuletzt aktualisierten Ergebnisse ausgedruckt 2. Obwohl die ersten beiden setState-Aufrufe nicht wirksam waren, wird in ihrem zweiten Parameter dennoch 2 gedruckt. Ersetzen Sie zu diesem Zeitpunkt den ersten Parameter von setState durch eine Funktion, und der Status vor der Aktualisierung kann über den ersten Parameter der Funktion abgerufen werden. importiere React, {Komponente} von „react“; Klasse Test erweitert Komponente { Konstruktor(Requisiten) { super(Requisiten); dieser.Zustand = { Anzahl: 1 }; } handleKlick = () => { this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }); Konsole.log(dieser.Zustand.Anzahl); } rendern() { zurückkehren ( <div Stil = {{ Breite: '100px', Höhe: '100px', Hintergrundfarbe: "gelb" }}> {dieser.Zustand.Anzahl} </div> ) } } Standardtest exportieren; Zu diesem Zeitpunkt ist das gedruckte Ergebnis 1, aber die auf der Seite angezeigte Anzahl ist 4. Es lässt sich feststellen, dass, wenn setState den Status durch Übergeben von Parametern aktualisiert, mehrere setState-Aktualisierungen nicht nur die letzte aktualisieren, sondern für mehrere Aktualisierungen wirksam sind. Als nächstes sehen wir, wie viel count in der zweiten Funktion gedruckt wird: importiere React, {Komponente} von „react“; Klasse Test erweitert Komponente { Konstruktor(Requisiten) { super(Requisiten); dieser.Zustand = { Anzahl: 1 }; } handleKlick = () => { this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { console.log('1', dieser.Zustand.Anzahl); }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { Konsole.log('2', dieser.Zustand.Anzahl); }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { Konsole.log('3', dieser.Zustand.Anzahl); }); Konsole.log(dieser.Zustand.Anzahl); } rendern() { zurückkehren ( <div Stil = {{ Breite: '100px', Höhe: '100px', Hintergrundfarbe: "gelb" }}> {dieser.Zustand.Anzahl} </div> ) } } Standardtest exportieren; Klicken Sie zu diesem Zeitpunkt einmal, und die Druckergebnisse in den drei SetState-Rückruffunktionen sind wie folgt. Wie Sie sich vorstellen können, ist das Seitenanzeigeergebnis ebenfalls 4
Fügen Sie den obigen Code in componentDidMount ein und das Ausgabeergebnis ist das gleiche wie oben. Denn wir können wissen, dass bei benutzerdefinierten synthetischen Ereignissen und Hook-Funktionen die Statusaktualisierung asynchron erfolgt. Aktualisieren Sie den Status in nativen Ereignissen synchron und setzen Sie das Timeout fest.Nehmen Sie setState in setTimeout als Beispiel importiere React, {Komponente} von „react“; Klasse Test erweitert Komponente { Konstruktor(Requisiten) { super(Requisiten); dieser.Zustand = { Anzahl: 1 }; } componentDidMount() { setzeTimeout(() => { dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }, () => { Konsole.log('1:', dieser.Zustand.Anzahl); }); dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }, () => { Konsole.log('2:', dieser.Zustand.Anzahl); }); dies.setState({ Anzahl: dieser.Zustand.Anzahl + 1 }, () => { Konsole.log('3:', dieser.Zustand.Anzahl); }); Konsole.log(dieser.Zustand.Anzahl); }, 0); } rendern() { zurückkehren ( <div Stil={{ Breite: '100px', Höhe: '100px', Hintergrundfarbe: "gelb" }}> {dieser.Zustand.Anzahl} </div> ) } } Standardtest exportieren; Zu diesem Zeitpunkt lauten die ausgedruckten Ergebnisse wie folgt:
Ersetzen Sie den ersten Parameter von setState durch eine Funktion: componentDidMount() { setzeTimeout(() => { this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { console.log('1', dieser.Zustand.Anzahl); }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { Konsole.log('2', dieser.Zustand.Anzahl); }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { Konsole.log('3', dieser.Zustand.Anzahl); }); Konsole.log(dieser.Zustand.Anzahl); }, 0); } Die ausgedruckten Ergebnisse sind dieselben wie oben. Glauben Sie, dass der Status vollständig steuerbar ist? In setTimeout werden mehrere setState-Aufrufe wirksam, und der aktualisierte Status kann im zweiten Parameter jedes setState abgerufen werden. Ebenso stimmen die Ausgabeergebnisse im nativen Ereignis mit denen in setTimeout überein und werden ebenfalls synchronisiert. importiere React, {Komponente} von „react“; Klasse Test erweitert Komponente { Konstruktor(Requisiten) { super(Requisiten); dieser.Zustand = { Anzahl: 1 }; } componentDidMount() { document.body.addEventListener('klicken', this.handleClick, false); } componentWillUnmount() { document.body.removeEventListener('klicken', this.handleClick, false); } handleKlick = () => { this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { console.log('1', dieser.Zustand.Anzahl); }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { Konsole.log('2', dieser.Zustand.Anzahl); }); this.setState((vorherigerState, Eigenschaften) => { return { Anzahl: vorherigerZustand.Anzahl + 1 } }, () => { Konsole.log('3', dieser.Zustand.Anzahl); }); Konsole.log(dieser.Zustand.Anzahl); } rendern() { zurückkehren ( <div Stil={{ Breite: '100px', Höhe: '100px', Hintergrundfarbe: "gelb" }} > {dieser.Zustand.Anzahl} </div> ) } } Standardtest exportieren; setState-bezogener QuellcodeDie folgenden Codes stammen alle aus der React17.0.2-Version Verzeichnis ./packages/react/src/ReactBaseClasses.js Funktion Komponente(Eigenschaften, Kontext, Updater) { this.props = Requisiten; dieser.Kontext = Kontext; // Wenn eine Komponente String-Referenzen hat, weisen wir später ein anderes Objekt zu. this.refs = leeresObjekt; // Wir initialisieren den Standard-Updater, aber der echte wird von dem // Renderer. this.updater = Updater || ReactNoopUpdateQueue; } Komponente.prototype.isReactComponent = {}; Komponente.prototype.setState = Funktion(Teilstatus, Rückruf) { invariant Typ des Teilzustands === 'Objekt' || Typ des Teilzustands === 'Funktion' || Teilzustand == null, 'setState(...): nimmt ein Objekt von Statusvariablen zum Aktualisieren oder ein ' + 'Funktion, die ein Objekt mit Zustandsvariablen zurückgibt.', ); this.updater.enqueueSetState(this, Teilstatus, Rückruf, „setState“); }; setState kann zwei Parameter empfangen. Der erste Parameter kann Objekt, Funktion, null oder undefiniert sein, und es wird kein Fehler ausgegeben. Führen Sie die unten stehende Methode this.updater.enqueueSetState aus. Durchsuchen Sie enqueueSetState global und finden Sie diese Variable in zwei Verzeichnissen. Zunächst der erste Satz Verzeichnisse: Verzeichnis ./packages/react/src/ReactNoopUpdateQueue.js Zeile 100, Methode „enqueueSetState“, die Parameter sind diese, initialisierter Status, Rückruf und Zeichenfolge „setState“, dies bezieht sich auf die aktuelle React-Instanz. enqueueSetState: Funktion( öffentliche Instanz, Teilzustand, Rückruf, Anrufername, ) { warnNoop(öffentliche Instanz, 'setState'); } Schauen Sie sich als nächstes die Methode warnNoop an: const didWarnStateUpdateForUnmountedComponent = {}; Funktion warnNoop(öffentlicheInstanz, Anrufername) { wenn (__DEV__) { const-Konstruktor = publicInstance.Konstruktor; const Komponentenname = (Konstruktor && (Konstruktor.Anzeigename || Konstruktor.Name)) || „ReagierenKlasse“; const Warnungsschlüssel = `${Komponentenname}.${Anrufername}`; if (didWarnStateUpdateForUnmountedComponent[warningKey]) { zurückkehren; } Konsole.Fehler( "%s kann für eine Komponente, die noch nicht gemountet ist, nicht aufgerufen werden." + 'Das ist ein No-Op, könnte aber auf einen Fehler in Ihrer Anwendung hinweisen. ' + 'Stattdessen direkt `this.state` zuweisen oder einen `state = {};` definieren ' + 'Klasseneigenschaft mit dem gewünschten Status in der %s-Komponente.', Anrufername, Komponentenname, ); didWarnStateUpdateForUnmountedComponent[warningKey] = true; } } Dieser Code entspricht dem Hinzufügen eines Attributs zum Objekt didWarnStateUpdateForUnmountedComponent. Der Schlüssel des Attributs ist die React-Komponente, die derzeit setState.setState ist. Wenn dieses Attribut vorhanden ist, wird es zurückgegeben; wenn dieses Attribut nicht vorhanden ist oder der Wert dieses Attributs false ist, wird der Wert dieses Attributs auf true gesetzt. Schauen wir uns ein anderes Verzeichnis an: Verzeichnis ./react-reconciler/src/ReactFiberClassComponent.new.js und ReactFiberClassComponent.old.js const classComponentUpdater = { enqueueSetState(inst, Nutzlast, Rückruf) { const fiber = getInstance(inst); const eventTime = requestEventTime(); const lane = requestUpdateLane(faser); const update = erstelleUpdate(eventTime, lane); update.payload = Nutzlast; wenn (Rückruf !== undefiniert && Rückruf !== null) { wenn (__DEV__) { warnOnInvalidCallback(Rückruf, 'setState'); } update.callback = Rückruf; } enqueueUpdate(Glasfaser, Update, Spur); const root = scheduleUpdateOnFiber(fiber, lane, eventTime); wenn (root !== null) { entangleTransitions(Wurzel, Faser, Spur); } wenn (__DEV__) { if (enableDebugTracing) { wenn (fiber.mode & DebugTracingMode) { const name = getComponentNameFromFiber(fiber) || 'Unbekannt'; logStateUpdateScheduled(Name, Spur, Nutzlast); } } } wenn (enableSchedulingProfiler) { markStateUpdateScheduled(Glasfaser, Spur); } } } Die Hauptfunktion ist enqueueUpdate.
Exportfunktion enqueueUpdate<Status>( Faser: Faser, Update: Update<Status>, Spur: Spur, ) { const updateQueue = fiber.updateQueue; wenn (updateQueue === null) { // Tritt nur auf, wenn die Faser ausgehängt wurde. zurückkehren; } const sharedQueue: SharedQueue<Status> = (updateQueue: beliebig).shared; wenn (isInterleavedUpdate(Faser, Spur)) { const interleaved = sharedQueue.interleaved; wenn (interleaved === null) { // Dies ist das erste Update. Erstellen Sie eine kreisförmige Liste. update.next = aktualisieren; // Am Ende des aktuellen Rendervorgangs werden die verschachtelten Updates dieser Warteschlange // in die Warteschlange übertragen werden. : pushInterleavedQueue(sharedQueue); } anders { update.next = verschachtelt.next; interleaved.next = aktualisieren; } sharedQueue.interleaved = aktualisieren; } anders { const ausstehend = sharedQueue.pending; wenn (ausstehend === null) { // Dies ist das erste Update. Erstellen Sie eine kreisförmige Liste. update.next = aktualisieren; } anders { update.next = ausstehend.next; ausstehend.nächstes = Aktualisierung; } sharedQueue.pending = aktualisieren; } wenn (__DEV__) { Wenn ( derzeitVerarbeitungswarteschlange === gemeinsam genutzteWarteschlange && !didWarnUpdateInsideUpdate ) { Konsole.Fehler( 'Ein Update (setState, replaceState oder forceUpdate) wurde geplant ' + 'aus einer Update-Funktion heraus. Update-Funktionen sollten rein sein, ' + ' ohne Nebenwirkungen. Erwägen Sie die Verwendung von componentDidUpdate oder einem ' + 'Rückruf.', ); didWarnUpdateInsideUpdate = wahr; } } } Wenn wir dies sehen, stellen wir fest, dass diese Methode das Update dieses Updates zur Update-Warteschlange hinzufügt, aber die Eigenschaft isBatchingUpdates wird in dieser Version nicht gefunden. Es scheint, dass die Änderungen an React Fiber ziemlich umfangreich sind, daher werde ich hier vorerst aufhören und mehr hinzufügen, wenn ich etwas Neues finde. Zusammenfassen
Oben finden Sie eine ausführliche Erklärung zu „React SetState“. Weitere Informationen zu „React SetState“ finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM! Das könnte Sie auch interessieren:
|
<<: Beispielanalyse zum Beheben von Problemen in historischen Linux-Images
>>: Eine detaillierte Diskussion über MySQL-Deadlocks und -Logs
Wichtige Punkte Mit der CSS-Eigenschaft „Größe än...
1. Warum müssen wir Tabellen und Partitionen auft...
Wenn nginx eine Anfrage empfängt, gleicht es zunä...
1. Informationen zur Installation von Docker find...
Umfassendes Verständnis des html.css-Überlaufs XM...
Die CSS3-Kategoriemenüeffekte sind wie folgt: HTM...
Konfigurieren Sie die Webseitenkomprimierung, um ...
Quelle des Problems Wie wir alle wissen, erzeugt ...
Die Schritte zum Verpacken einer Python-Umgebung ...
Dies ist ein Artikel über die Benutzerfreundlichk...
1. Nginx-Installationsschritte 1.1 Offizielle Web...
Einführung in jQuery Die jQuery-Bibliothek kann e...
Die meisten Navigationsleisten sind horizontal an...
Standardmäßig werden Prozesse im Container mit Ro...
In einem aktuellen Unternehmen besteht die Anford...