Co się dzieje podczas wielokrotnego używania this.setState w komponencie React?

102

Chciałem sprawdzić, co się dzieje, gdy używasz this.setState wiele razy (2 razy na potrzeby dyskusji). Myślałem, że komponent zostanie wyrenderowany dwukrotnie, ale najwyraźniej jest renderowany tylko raz. Innym oczekiwaniem było to, że może drugie wywołanie setState przejdzie przez pierwsze, ale zgadłeś - działało dobrze.

Link do JSfiddle

var Hello = React.createClass({
  render: function() {
    return (
      <div>
        <div>Hello {this.props.name}</div>
        <CheckBox />
      </div>
    );
  }
});

var CheckBox = React.createClass({
  getInitialState: function() {
    return {
      alex: 0
    };
  },

  handleChange: function(event) {
    this.setState({
      value: event.target.value
    });
    this.setState({
      alex: 5
    });
  },

  render: function() {
    alert('render');
    return (
      <div>
        <label htmlFor="alex">Alex</label>
        <input type="checkbox" onChange={this.handleChange} name="alex" />
        <div>{this.state.alex}</div>
      </div>
    );
  }
});

ReactDOM.render(
  <Hello name="World" />,
  document.getElementById('container')
);

Jak zobaczysz, przy każdym renderowaniu pojawia się alert z napisem „renderuj”.

Czy masz wyjaśnienie, dlaczego to działało prawidłowo?

alexunder
źródło
2
React jest całkiem sprytny i będzie renderował się ponownie tylko wtedy, gdy stan wymagany do renderowania zostanie zmieniony. W swojej metodzie renderowania używasz tylko this.state.alex- co się stanie, jeśli dodasz element, od którego również zależy this.state.value?
Martin Wedvich
1
@MartinWedvich Zepsuje się, jeśli będę od tego zależny. W tym celu masz metodę `` getInitialState '' - aby ustawić wartości domyślne, aby Twoja aplikacja nie uległa awarii.
alexunder
1
powiązane i przydatne: stackoverflow.com/questions/48563650/…
andydavies

Odpowiedzi:

118

Reaguj aktualizacje stanu partii, które występują w programach obsługi zdarzeń i metodach cyklu życia. Dlatego jeśli wielokrotnie aktualizujesz stan w <div onClick />module obsługi, React będzie czekał na zakończenie obsługi zdarzenia przed ponownym renderowaniem.

Aby było jasne, działa to tylko w kontrolowanych przez React syntetycznych programach obsługi zdarzeń i metodach cyklu życia. Na przykład aktualizacje stanu nie są grupowane w programach AJAX i setTimeoutprogramach obsługi zdarzeń.

Chris Gaudreau
źródło
1
Dzięki, to ma sens, każdy pomysł, jak to się robi za kulisami?
alexunder
13
Ponieważ React w pełni kontroluje syntetyczne procedury obsługi zdarzeń (takie jak <div onClick />) i metody cyklu życia komponentów, wie, że może bezpiecznie zmienić zachowanie setStatena czas trwania wywołania aktualizacji stanu wsadowego i czekać na opróżnienie. W setTimeoutprzeciwieństwie do AJAX lub programu obsługi, React nie ma możliwości dowiedzenia się, kiedy nasz program obsługi zakończył wykonywanie. Technicznie rzecz biorąc, React kolejkuje aktualizacje stanu w obu przypadkach, ale opróżnia je natychmiast poza program obsługi kontrolowany przez React.
Chris Gaudreau
1
@neurosnap Nie sądzę, aby dokumentacja szczegółowo omawiała ten temat. Jest on omówiony abstrakcyjnie w sekcji State and Lifecycle oraz w dokumentacji setState . Zobacz kod dla ReactDefaultBatchingStrategy , która jest obecnie jedyną oficjalną strategią wsadową .
Chris Gaudreau
2
@BenjaminToueg Wierzę, że możesz użyć ReactDOM.unstable_batchedUpdates(function)do wsadowania setStatepoza programami obsługi zdarzeń React. Jak sama nazwa wskazuje, utracisz gwarancję.
Chris Gaudreau
1
zawsze dobrze jest
odwołać się
33

Metoda setState () nie aktualizuje natychmiast stanu komponentu, po prostu umieszcza aktualizację w kolejce do przetworzenia później. React może grupować wiele żądań aktualizacji, aby renderowanie było bardziej wydajne. Z tego powodu przy próbie aktualizacji stanu w oparciu o poprzedni stan składnika należy zachować szczególne środki ostrożności.

Na przykład poniższy kod zwiększy atrybut wartości stanu tylko o 1, mimo że został wywołany 4 razy:

 class Counter extends React.Component{
   constructor(props){
     super(props)
    //initial state set up
     this.state = {value:0}
   }
   componentDidMount(){
    //updating state
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
    this.setState({value:this.state.value+1})
   }
   render(){
    return <div>Message:{this.state.value}</div>
   }
}

Aby użyć stanu po jego aktualizacji, wykonaj całą logikę w argumencie callback:

//this.state.count is originally 0
this.setState({count:42}, () => {
  console.log(this.state.count)
//outputs 42
})

Metoda setState (updater, [callback]) może przyjąć funkcję aktualizującą jako pierwszy argument do aktualizacji stanu na podstawie poprzedniego stanu i właściwości. Wartość zwracana funkcji aktualizującej zostanie płytko scalona z poprzednim stanem komponentu. Metoda aktualizuje stan asynchronicznie, więc istnieje wywołanie zwrotne opcji, które zostanie wywołane po zakończeniu pełnej aktualizacji stanu.

Przykład:

this.setState((prevState, props) => { 
return {attribute:"value"}
})

Oto przykład, jak zaktualizować stan na podstawie poprzedniego stanu:

    class Counter extends React.Component{
      constructor(props) {
        super(props)
    //initial state set up
        this.state = {message:"initial message"}
    }
      componentDidMount() {
    //updating state
        this.setState((prevState, props) => {
          return {message: prevState.message + '!'}
        })
     }
     render(){
       return <div>Message:{this.state.message}</div>
     }
  }
Biboswan
źródło
Ach, nie wiedziałem o tym setState(updater,[callback]), to jest jak mniej szczegółowe Object.assigndzięki za to!
AJ Venturella
1
@Biboswan, cześć, jestem nowy w React i chciałem cię zapytać, czy to prawda, że ​​wszystkie cztery setStates w metodzie CompountDidMount są scalane i TYLKO OSTATNIE setState zostanie wykonane, ale pozostałe 3 zostaną zignorowane?
Dickens,