Wiem, że React może przeprowadzać aktualizacje stanu asynchronicznie i wsadowo w celu optymalizacji wydajności. Dlatego nigdy nie możesz ufać, że stan zostanie zaktualizowany po wywołaniu setState
. Ale można zaufać React aby zaktualizować stan w tej samej kolejności, jak setState
nazywany jest przez
- ten sam składnik?
- różne komponenty?
W poniższych przykładach rozważ kliknięcie przycisku:
1. Czy kiedykolwiek istnieje możliwość, że a jest fałszywe i b jest prawdziwe dla:
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { a: false, b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.setState({ a: true });
this.setState({ b: true });
}
}
2. Czy kiedykolwiek istnieje możliwość, że a jest fałszywe i b jest prawdziwe dla:
class SuperContainer extends React.Component {
constructor(props) {
super(props);
this.state = { a: false };
}
render() {
return <Container setParentState={this.setState.bind(this)}/>
}
}
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { b: false };
}
render() {
return <Button onClick={this.handleClick}/>
}
handleClick = () => {
this.props.setParentState({ a: true });
this.setState({ b: true });
}
}
Pamiętaj, że są to skrajne uproszczenia mojego przypadku użycia. Zdaję sobie sprawę, że mogę to zrobić inaczej, np. Aktualizację obu parametrów stanu w tym samym czasie w przykładzie 1, a także wykonanie aktualizacji drugiego stanu w wywołaniu zwrotnym aktualizacji pierwszego stanu w przykładzie 2. Jednak to nie jest moje pytanie, interesuje mnie tylko to, czy istnieje dobrze zdefiniowany sposób, w jaki React przeprowadza te aktualizacje stanu, nic więcej.
Każda odpowiedź poparta dokumentacją jest bardzo mile widziana.
źródło
Odpowiedzi:
Pracuję nad Reactem.
TLDR:
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śliChild
iParent
każde wywołaniesetState()
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. Jaka
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ć zthis.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żdysetState()
byłby przetwarzany natychmiast po tym, jak to się dzieje. W tym przypadku, tak, by zobaczyć stan pośredni:promise.then(() => { // We're not in an event handler, so these are flushed separately. this.setState({a: true}); // Re-renders with {a: true, b: false } this.setState({b: true}); // Re-renders with {a: true, b: true } this.props.setParentState(); // Re-renders the parent });
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(() => { // Forces batching ReactDOM.unstable_batchedUpdates(() => { this.setState({a: true}); // Doesn't re-render yet this.setState({b: true}); // Doesn't re-render yet this.props.setParentState(); // Doesn't re-render yet }); // When we exit unstable_batchedUpdates, re-renders once });
Wszystkie wewnętrzne procedury obsługi zdarzeń React są opakowane,
unstable_batchedUpdates
dlatego są domyślnie grupowane. Zwróć uwagę, żeunstable_batchedUpdates
dwukrotne zawinięcie aktualizacji nie ma żadnego efektu. Aktualizacje są opróżniane, gdy wychodzimy zunstable_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ć.
źródło
obj.a = true; obj.b = true
), A na końcu po prostu to zrobićthis.setState(obj)
. Jest to bezpieczne niezależnie od tego, czy znajdujesz się w programie obsługi zdarzeń, czy nie. Może to być niezła sztuczka, jeśli często popełniasz błąd, ustawiając stan kilka razy poza programami obsługi zdarzeń.componentDidUpdate
i inne wywołania zwrotne cyklu życia od React 16? Z góry dziękuję!To właściwie dość interesujące pytanie, ale odpowiedź nie powinna być zbyt skomplikowana. Jest taki świetny artykuł na medium, który zawiera odpowiedź.
1) Jeśli to zrobisz
this.setState({ a: true }); this.setState({ b: true });
Nie sądzę, żeby doszło do sytuacji, w której
a
będzietrue
ib
będzie zfalse
powodu wsadowania .Jednak jeśli
b
zależy naa
czym tam rzeczywiście może być sytuacja, w której nie byłoby uzyskać oczekiwany stan.// assuming this.state = { value: 0 }; this.setState({ value: this.state.value + 1}); this.setState({ value: this.state.value + 1}); this.setState({ value: this.state.value + 1});
Po przetworzeniu wszystkich powyższych wywołań
this.state.value
będzie 1, a nie 3, jak można się spodziewać.Jest to wspomniane w artykule:
setState accepts a function as its parameter
// assuming this.state = { value: 0 }; this.setState((state) => ({ value: state.value + 1})); this.setState((state) => ({ value: state.value + 1})); this.setState((state) => ({ value: state.value + 1}));
To nam da
this.state.value === 3
źródło
this.state.value
jest aktualizowany zarówno w programach obsługi zdarzeń (gdziesetState
jest wsadowy), jak i wywołaniach zwrotnych AJAX (gdzie niesetState
jest wsadowy). W programach obsługi zdarzeń użyłbym, aby zawsze mieć pewność, że aktualizuję stan przy użyciu aktualnie zaktualizowanego stanu dostarczonego przez funkcję. Czy powinienem używać funkcji aktualizującej w kodzie wywołania zwrotnego AJAX, nawet jeśli wiem, że nie jest to partia? Czy mógłbyś wyjaśnić użycie w wywołaniu zwrotnym AJAX z lub bez funkcji aktualizatora? Dziękuję Ci!updater function
setState
setState
setState
zostaną straceni, ale ostatni wygra.Wiele połączeń w tym samym cyklu może być połączonych razem. Na przykład, jeśli spróbujesz zwiększyć ilość towaru więcej niż raz w tym samym cyklu, da to równowartość:
Object.assign( previousState, {quantity: state.quantity + 1}, {quantity: state.quantity + 1}, ... )
https://reactjs.org/docs/react-component.html
źródło
jak w dok
dokona zmiany jak w kolejce ( FIFO : First In First Out) pierwsze wywołanie będzie wykonane jako pierwsze
źródło