setState асинхронный, синхронный и расширенный

React.js

Как использовать setState

При повседневном использовании React очень важным моментом является не изменять состояние напрямую. Например:this.state.count = 1Невозможно запустить React для обновления представления. Потому что механизм React предусматривает, что для обновления состояния необходимо сначала вызвать метод setState.

this.setState({
    count: 1
})

Это вызовет повторный рендеринг. Немного опытный разработчик знает, что метод setState на самом деле является "асинхронным". Сразу после выполнения получить последнее состояние напрямую невозможно, нужно пройти через React, чтобы слить все изменения в состояние перед вычислением нового виртуального DOM, а затем перерендерить реальный DOM по последнему виртуальному DOM.

class App extends Component {
	state = {
        count: 0
	}

    componentDidMount(){
        this.setState({count: this.state.count + 1})
        console.log(this.state.count) // 0
    }

    render(){
        ...
    }
}

демо, пожалуйста, нажмите

Итак, как я могу получить модифицированное состояние? React предоставляет нам обратный вызов для реализации.

...
this.setState({count: this.state.count + 1}, ()=>{
    console.log(this.state.count) // 1
})
...

Состояние в обратном вызове является последним, поскольку время выполнения обратного вызова наступает после слияния состояния. Если мы сделаем это:

...
	this.setState({count: this.state.count + 1})
	this.setState({count: this.state.count + 1})
...

Фактический окончательный счет будет равен 1, причина в том, чтоthis.state.count = 0. Итак, как добиться результата 2?

...
this.setState(prevState => {count: prevState.count + 1});
this.setState(prevState => {count: prevState.count + 1});
...

setState()На самом деле он может принимать в качестве параметра функцию, а первый параметр функции — это последнее состояние.

представленный вышеsetStateТри способа использования, давайте посмотрим на время их выполнения:

...
	this.setState({ count: this.state.count + 1 });
    console.log("console: " + this.state.count); // 0
    this.setState({ count: this.state.count + 1 }, () => {
      console.log("console from callback: " + this.state.count); // 2
    });
    this.setState(prevState => {
      console.log("console from func: " + prevState.count); // 1
      return {
        count: prevState.count + 1
      };
    }, ()=>{
      console.log('last console: '+ this.state.count)
    });
...

Результаты:

console: 0 
console from func: 1 
console from callback: 2
last console: 2 

React фактически поддерживает очередь обновления состояния. Каждый раз, когда вызывается setState, текущее измененное состояние будет помещено в очередь. В конце React объединит очередь, а затем выполнит обратный вызов. Перейдите к следующему процессу (обновление виртуального дома, запуск рендеринга) в соответствии с окончательным результатом слияния.

Почему setState разработан как асинхронный?

потому чтоsetState()После этого последнее состояние нельзя получить сразу, и такое ощущение, что задаешь состояние асинхронно. Действительно есть ощущение асинхронности (фактический принцип будет объяснен позже). Так почему же состояние дизайна React обновляется таким образом? непосредственныйthis.state.count = 1 Разве это не хорошо?

Если вы заинтересованы, пожалуйста, нажмите здесь:GitHub.com/Facebook/Горячие…

Вот краткий обзор:

  • Обеспечить внутреннюю консистенцию: дажеstateсинхронное обновление,propsНи один. (Вы узнаете только тогда, когда родительский компонент повторно отобразитprops)
  • БудуstateОтсрочка обновления DOM до окончательного слияния пакетов и последующий рендеринг очень полезны для оптимизации производительности приложения.Если реальный DOM повторно рендерится каждый раз при изменении состояния, это приведет к огромному потреблению производительности.

Является ли setState действительно асинхронным?

Давайте сначала посмотрим на кусок кода. Прежде чем выполнять его, рекомендуется сначала оценить результат:

демо, пожалуйста, нажмите

class App extends Component {
  state = {
    count: 0
  };

  componentDidMount() {
    // 生命周期中调用
    this.setState({ count: this.state.count + 1 });
    console.log("lifecycle: " + this.state.count);
    setTimeout(() => {
      // setTimeout中调用
      this.setState({ count: this.state.count + 1 });
      console.log("setTimeout: " + this.state.count);
    }, 0);
    document.getElementById("div2").addEventListener("click", this.increment2);
  }

  increment = () => {
    // 合成事件中调用
    this.setState({ count: this.state.count + 1 });
    console.log("react event: " + this.state.count);
  };

  increment2 = () => {
    // 原生事件中调用
    this.setState({ count: this.state.count + 1 });
    console.log("dom event: " + this.state.count);
  };

  render() {
    return (
      <div className="App">
        <h2>couont: {this.state.count}</h2>
        <div id="div1" onClick={this.increment}>
          click me and count+1
        </div>
        <div id="div2">click me and count+1</div>
      </div>
    );
  }
}

Прежде чем обсуждать, давайте кратко разберемся с механизмом событий реакции: чтобы решить проблемы кроссплатформенности и совместимости, реакция сама инкапсулирует набор механизмов событий и проксирует собственные события, как вjsxобщий вonClick,onChangeЭто синтетические события.

Затем указанные выше 4 способа вызоваsetState(), с последующим получением последнего состояния.Согласно упомянутому ранее асинхронному принципу, оно не должно быть доступно. Однако,setTimeoutЕсли он вызывается посередине и является родным событием, последнее состояние можно получить сразу. Основная причина заключается в том, что setState на самом деле не является асинхронной операцией, она просто имитирует асинхронное поведение. React сохранит идентичность (isBatchingUpdates), чтобы определить, следует ли обновить напрямую или временно сохранить состояние в очереди.setTimeoutА нативные события будут напрямую обновлять состояние, поэтому вы можете сразу получить самое последнее состояние. Синтетические события и функции жизненного цикла React контролируются React, которыйisBatchingUpdatesУстановить какtrue, что похоже на асинхронный набор.

Суммировать

Резюме здесь является прямой цитатой:nuggets.capable/post/684490…

  1. setState"Асинхронный" только в синтетических событиях и хуках, в нативных событиях иsetTimeoutсинхронизированы.
  2. setState«Асинхронность» не означает, что внутренняя реализация реализована асинхронным кодом, на самом деле процесс и код, исполняемый сам по себе, являются синхронными, но последовательность вызова синтетических событий и функций-ловушек предшествует обновлению, так что синтетические события а хуки-функции сразу получить нельзя.К обновленному значению формируется так называемая "асинхронность".Конечно, обновленный результат можно получить через callback во втором параметре setState(partialState, callback).
  3. setStateОптимизация пакетного обновления также основана на «асинхронности» (синтетические события, функции ловушек) и не будет обновляться пакетами в собственных событиях и setTimeout.В «асинхронном», если одно и то же значение повторяется несколько разsetState,setStateСтратегия пакетного обновления перезапишет его, возьмет последнее выполнение, если оно одновременноsetStateНесколько отдельных значений, которые объединяются и обновляются пакетами при обновлении.

Ссылаться на:

Откровенно говоря, мой уровень еще довольно ограничен, и мне еще нужно много и упорно работать. Вы можете писать осмысленные вещи, только если много знаете. Большая часть того, что я пишу сейчас, основана на чужих статьях, и после того, как я это пойму, я смогу снова выразить это на своем родном языке. .

Не торопитесь, однажды вы сможете преодолеть этот предел и действительно написать что-то, что принадлежит вам.

Также, искренняя благодарность авторам этих статей за их помощь.