Dlaczego wywoływanie metody reagowania setState nie powoduje natychmiastowej mutacji stanu?

326

Czytam sekcję Formularzedokumentacja i właśnie wypróbowałem ten kod, aby zademonstrować onChangeużycie ( JSBIN ).

var React= require('react');

var ControlledForm= React.createClass({
    getInitialState: function() {
        return {
            value: "initial value"
        };
    },

    handleChange: function(event) {
        console.log(this.state.value);
        this.setState({value: event.target.value});
        console.log(this.state.value);

    },

    render: function() {
        return (
            <input type="text" value={this.state.value} onChange={this.handleChange}/>
        );
    }
});

React.render(
    <ControlledForm/>,
  document.getElementById('mount')
);

Kiedy aktualizuję <input/>wartość w przeglądarce, druga console.logczęść handleChangeoddzwaniania drukuje to samo valueco pierwsza console.log, dlaczego nie widzę wyniku this.setState({value: event.target.value})w zakresie handleChangeoddzwaniania?

tarrsalah
źródło
Jeśli używasz haków, spójrz na metodę set useState, która nie odzwierciedla natychmiast zmiany .
Emile Bergeron,

Odpowiedzi:

615

Z dokumentacji React :

setState()nie mutuje natychmiast, this.stateale tworzy oczekujące przejście stanu. Dostęp this.statepo wywołaniu tej metody może potencjalnie zwrócić istniejącą wartość. Nie ma gwarancji synchronicznego działania połączeń na setStatei połączenia mogą być grupowane w celu zwiększenia wydajności.

Jeśli chcesz, aby funkcja była wykonywana po zmianie stanu, przekaż ją jako wywołanie zwrotne.

this.setState({value: event.target.value}, function () {
    console.log(this.state.value);
});
Michael Parker
źródło
Dobra odpowiedź. Obserwacja, którą muszę zrobić, jest ostrożna przy korzystaniu z valueLink. Działa dobrze, jeśli nie musisz formatować / maskować wejścia.
Dherik,
42
Możesz także sprawdzić componentDidUpdate. Zostanie wywołany po zmianie stanu.
Keysox
1
Szybkie pytanie, czy mogę, widzę, że kiedy przekażemy funkcję, której potrzebujemy jako wywołanie zwrotne do setState, miałem nadzieję, że func zostanie wykonany jako pierwszy przed wywołaniem render (). Ale widzę, że kolejność jest ustawiona w setState () -> render () -> callback () setStates. czy to normalne? Co jeśli chcemy kontrolować nasz rendering w oparciu o rzeczy, które robimy w wywołaniu zwrotnym? shouldComponentUpdate ?
semuzaboi
2
Zmiana stanu komponentu zawsze wyzwala ponowne renderowanie, chyba że zachowane jest shouldComponentUpdateinaczej. Co dokładnie próbujesz zrobić w przekazanym wywołaniu zwrotnym, setStatektóre chcesz wykonać przed ponownym renderowaniem?
Michael Parker
4
...dlaczego? Czy ktoś mógłby to uzasadnić?
JackHasaKeyboard
49

Jak wspomniano w dokumentacji React, nie ma gwarancji setStatesynchronicznego odpalenia, więc console.logmożesz zwrócić stan przed aktualizacją.

Michael Parker wspomina o przekazaniu oddzwonienia w obrębie setState. Innym sposobem obsługi logiki po zmianie stanu jest componentDidUpdatemetoda cyklu życia, która jest metodą zalecaną w dokumentach React.

Zasadniczo zalecamy zamiast tego użycie componentDidUpdate () dla takiej logiki.

Jest to szczególnie przydatne, gdy mogą być setStateuruchamiane kolejne s, a chcesz uruchomić tę samą funkcję po każdej zmianie stanu. Zamiast dodawać do każdego wywołanie zwrotne setState, możesz umieścić funkcję wewnątrz componentDidUpdate, z określoną logiką w razie potrzeby.

// example
componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.foo();  
  }
}
Yo Wakita
źródło
16

Możesz spróbować użyć ES7 async / czekaj. Na przykład używając twojego przykładu:

handleChange: async function(event) {
    console.log(this.state.value);
    await this.setState({value: event.target.value});
    console.log(this.state.value);
}
kurokiiru
źródło
Czym różni się twoja odpowiedź od odpowiedzi wysokiej jakości?
tod
9
Inna odpowiedź dotyczy używania wywołania zwrotnego w setState (). Pomyślałem, że umieściłem to tutaj dla tych, którzy nie mają zastosowania w przypadku wywołania zwrotnego Na przykład, gdy sam zmagałem się z tym problemem, mój przypadek użycia dotyczył przypadku przełącznika w zaktualizowanym stanie zaraz po jego ustawieniu. Dlatego użycie async / await było lepsze niż użycie wywołania zwrotnego.
kurokiiru
czy wpłynie to na wydajność, jeśli zawsze używam, czekam zawsze, kiedy chcę zaktualizować jakiś stan, a następnie czekam na jego aktualizację? A jeśli ustawię wiele oczekujących na setStates w łańcuchu jeden pod drugim, czy będzie renderowany po każdej aktualizacji setState? czy po ostatniej aktualizacji setState?
user2774480,
Na pytanie o użytkownika 2774480 uważam, że wszystko sprowadza się do konkretnego przypadku użycia, aby określić, której implementacji użyć. Jeśli w łańcuchu używanych jest wiele setState, wpłynie to na wydajność i tak, będzie renderowane po każdym setState, ale popraw mnie, jeśli się mylę.
kurokiiru,
-1

async-await składnia działa idealnie dla czegoś takiego jak poniżej ...

changeStateFunction = () => {
  // Some Worker..

  this.setState((prevState) => ({
  year: funcHandleYear(),
  month: funcHandleMonth()
}));

goNextMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

goPrevMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}
Ritwik
źródło
-1

Mówiąc prosto - this.setState ({data: wartość}) ma charakter asynchroniczny, co oznacza, że ​​wychodzi ze stosu wywołań i wraca do stosu wywołań, chyba że zostanie rozwiązany.

Przeczytaj o pętli zdarzeń, aby uzyskać wyraźny obraz natury asynchronicznej w JS i powodów, dla których aktualizacja wymaga czasu -

https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

W związku z tym -

    this.setState({data:value});
    console.log(this.state.data); // will give undefined or unupdated value

ponieważ aktualizacja wymaga czasu. Aby osiągnąć powyższy proces -

    this.setState({data:value},function () {
     console.log(this.state.data);
    });
Vishal Bisht
źródło