Pracuję nad Reactem.
TLDR:
Ale czy możesz zaufać Reactowi, że zaktualizuje stan w tej samej kolejności, w jakiej jest wywoływana funkcja setState
Tak.
Tak.
Zamówienie o aktualizacjach zawsze jest przestrzegane. To, czy widzisz stan pośredni „między” nimi, czy nie, zależy od tego, czy jesteś w grupie, czy nie.
Obecnie (React 16 i wcześniejsze) tylko aktualizacje wewnątrz obsługi zdarzeń React są domyślnie grupowane . Istnieje niestabilny interfejs API, który wymusza przetwarzanie wsadowe poza programami obsługi zdarzeń w rzadkich przypadkach, gdy jest to potrzebne.
W przyszłych wersjach (prawdopodobnie React 17 i późniejszych), React będzie domyślnie grupował wszystkie aktualizacje, więc nie będziesz musiał o tym myśleć. Jak zawsze, będziemy ogłaszać wszelkie zmiany w tym zakresie na blogu React oraz w informacjach o wydaniu.
Kluczem do zrozumienia tego jest to, że bez względu na to, ile setState()
wywołań w ilu składnikach wykonujesz w module obsługi zdarzeń React , po zakończeniu zdarzenia wygenerują tylko jedno ponowne renderowanie . Ma to kluczowe znaczenie dla dobrej wydajności w dużych aplikacjach, ponieważ jeśli Child
i Parent
każde wywołanie setState()
podczas obsługi zdarzenia kliknięcia, nie chcesz ponownie renderować Child
dwukrotnie.
W obu twoich przykładach setState()
wywołania zachodzą wewnątrz procedury obsługi zdarzeń React. Dlatego zawsze są one przepłukiwane razem na koniec zdarzenia (i nie widzisz stanu pośredniego).
Aktualizacje są zawsze płytko scalane w kolejności, w jakiej występują . Więc jeśli pierwsza aktualizacja to {a: 10}
, druga to {b: 20}
, a trzecia to {a: 30}
, renderowany stan będzie {a: 30, b: 20}
. Nowsza aktualizacja tego samego klucza stanu (np. Jak a
w moim przykładzie) zawsze „wygrywa”.
this.state
Obiekt zostanie zaktualizowany, gdy ponownie renderować UI pod koniec partii. Jeśli więc chcesz zaktualizować stan na podstawie poprzedniego stanu (na przykład zwiększając licznik), powinieneś użyć setState(fn)
wersji funkcjonalnej, która daje poprzedni stan, zamiast czytać z this.state
. Jeśli jesteś ciekawy, dlaczego tak się dzieje, wyjaśniłem to szczegółowo w tym komentarzu .
W twoim przykładzie nie zobaczylibyśmy „stanu pośredniego”, ponieważ znajdujemy się w module obsługi zdarzeń React, w którym jest włączone przetwarzanie wsadowe (ponieważ React „wie”, kiedy wychodzimy z tego zdarzenia).
Jednak zarówno w React 16, jak i we wcześniejszych wersjach, nie ma domyślnego przetwarzania wsadowego poza programami obsługi zdarzeń React . Więc jeśli w twoim przykładzie zamiast procedury obsługi odpowiedzi AJAX handleClick
, każdy setState()
byłby przetwarzany natychmiast po tym, jak to się dzieje. W tym przypadku, tak, by zobaczyć stan pośredni:
promise.then(() => {
this.setState({a: true});
this.setState({b: true});
this.props.setParentState();
});
Zdajemy sobie sprawę, że to niewygodne, że zachowanie różni się w zależności od tego, czy jesteś w programie obsługi zdarzeń, czy nie . Zmieni się to w przyszłej wersji React, która będzie domyślnie grupować wszystkie aktualizacje (i zapewni API opt-in do synchronicznego opróżniania zmian). Dopóki nie zmienimy domyślnego zachowania (potencjalnie w Reakcie 17), istnieje API, którego możesz użyć do wymuszenia grupowania :
promise.then(() => {
ReactDOM.unstable_batchedUpdates(() => {
this.setState({a: true});
this.setState({b: true});
this.props.setParentState();
});
});
Wszystkie wewnętrzne procedury obsługi zdarzeń React są opakowane, unstable_batchedUpdates
dlatego są domyślnie grupowane. Zwróć uwagę, że unstable_batchedUpdates
dwukrotne zawinięcie aktualizacji nie ma żadnego efektu. Aktualizacje są opróżniane, gdy wychodzimy z unstable_batchedUpdates
wywołania zewnętrznego .
Ten interfejs API jest „niestabilny” w tym sensie, że usuniemy go, gdy przetwarzanie wsadowe jest już domyślnie włączone. Jednak nie usuniemy go w pomniejszej wersji, więc możesz bezpiecznie na nim polegać aż do React 17, jeśli chcesz wymusić wsadowanie w niektórych przypadkach poza programami obsługi zdarzeń React.
Podsumowując, jest to mylący temat, ponieważ React domyślnie umieszcza tylko partie wewnątrz programów obsługi zdarzeń. Zmieni się to w przyszłych wersjach i wówczas zachowanie będzie prostsze. Rozwiązaniem nie jest jednak partie mniejsze , ale domyślnie większe partie . To właśnie zamierzamy zrobić.