Подтвердить что ты не робот

Почему setState в реале Async вместо Sync?

Я только что обнаружил, что в реакции this.setState() функция в любом компоненте является асинхронной или вызывается после завершения функции, в которой она была вызвана.

Теперь я искал и нашел этот блог (setState() Операция мутации состояния может быть синхронной в ReactJS)

Здесь он обнаружил, что setState является асинхронным (вызывается, когда стек пуст) или синхронизируется (вызывается сразу после вызова), в зависимости от того, как было инициировано изменение состояния.

Теперь эти две вещи трудно переварить.

  1. В блоге функция setState вызывается внутри функции updateState, но то, что вызвало функцию updateState, не является тем, о чем должна знать вызываемая функция.
  2. Почему они делают setState асинхронным, поскольку JS является однопоточным языком, и этот setState не является вызовом WebAPI или сервера, поэтому должен выполняться только в потоке JS. Делают ли они это так, чтобы повторный рендеринг не останавливал всех слушателей событий и прочее, или есть какая-то другая проблема дизайна.
4b9b3361

Ответ 1

Вы можете вызвать функцию после обновления значения состояния:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

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

Вместо:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Просто сделай это:

this.setState({
    foo: "one",
    bar: "two"
});

Ответ 2

1) Действия setState являются асинхронными и пакетируются для увеличения производительности. Это объясняется в документации setState.

setState() не изменяет немедленно this.state, но создает ожидающий переход в состояние. Доступ к this.state после вызова этого метода может потенциально вернуть существующее значение. Нет гарантии синхронной работы вызовов setState, и вызовы могут быть сгруппированы для повышения производительности.


2) Почему они делают setState асинхронным, поскольку JS является однопоточным языком, а этот setState не является вызовом WebAPI или сервера?

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

Таким образом, вызовы setState являются асинхронными, а также пакетными для улучшения пользовательского интерфейса и производительности.

Ответ 3

Я знаю, что этот вопрос старый, но он вызывает много путаницы для многих пользователей-реакционеров в течение длительного времени, включая меня. Недавно Дэн Абрамов (из реакционной команды) просто написал замечательное объяснение, почему природа setState асинхронна:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setState должен быть асинхронным, и есть несколько действительно веских причин для этого в связанном объяснении Дэн Абрамов. Это не означает, что он всегда будет асинхронным - это в основном означает, что вы просто не можете зависеть от его синхронности. ReactJS учитывает многие переменные в сценарии, в котором вы меняете состояние, чтобы решить, когда state должно быть обновлено, а ваш компонент повторно удален.
Простой пример, демонстрирующий это, заключается в том, что если вы вызываете setState как реакцию на действие пользователя, то state, вероятно, будет немедленно обновлено (хотя, опять же, вы не можете рассчитывать на него), поэтому пользователь не почувствует любая задержка, но если вы вызываете setState в ответ на ответ на вызов ajax или какое-либо другое событие, которое не запускается пользователем, тогда состояние может быть обновлено с небольшой задержкой, так как пользователь не почувствует эту задержку, и это улучшит производительность, ожидая одновременного пакетного обновления состояния и повторного DOM меньше.

Ответ 4

Хорошая статья здесь https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

или передать обратный вызов this.setState({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

Ответ 5

Представьте прирост счетчика в некотором компоненте:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

Существует обработчик счетчика, прикрепленный как к родительскому, так и к дочерним компонентам. Это сделано намеренно, поэтому мы можем выполнить setState() дважды в одном и том же клик-событии, но из двух разных обработчиков.

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

Поэтому сначала запускается btnCountHandler(), ожидая, что он увеличит счетчик до 1, а затем выполнит команду divCountHandler(), чтобы увеличить счет до 2.

Однако счетчик только увеличивается до 1, как вы можете проверить в инструментах React Developer.

Это доказывает, что реагируют

  • очереди всех вызовов setState

  • возвращается в эту очередь после выполнения последнего метода в контексте (в этом случае divCountHandler)

  • объединяет все мутации объектов, происходящие в нескольких вызовах setState, в том же контексте (все вызовы методов внутри одной фазы события - это один и тот же контекст, например), в один синтаксис мутации объекта (слияние имеет смысл, потому что именно поэтому мы можем обновлять свойства состояния независимо в setState() в первую очередь)

  • и передает его в один setState(), чтобы предотвратить повторный рендеринг из-за множества вызовов setState() (это очень примитивное описание пакетной обработки).

Результирующий код работает от реакции:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

Чтобы остановить это поведение, вместо передачи объектов в качестве аргументов в метод setState, обратные вызовы передаются.

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

После того, как последний метод завершит выполнение и когда реакция вернется для обработки очереди setState, он просто вызывает обратный вызов для каждого поставленного набора, передавая его в предыдущее состояние компонента.

Этот способ реагирует на то, что последний обратный вызов в очереди получает обновление состояния, которое все предыдущие коллеги положили в руки.

Ответ 6

Вы можете использовать следующую переноску, чтобы сделать синхронизирующий вызов

this.setState((state =>{
  return{
    something
  }
})

Ответ 7

Да, setState() является асинхронным.

По ссылке: https://reactjs.org/docs/react-component.html#setstate

  • React не гарантирует, что изменения состояния будут применены немедленно.
  • setState() не всегда сразу обновляет компонент.
  • Думайте о setState() как о запросе, а не как о немедленной команде для обновления компонента.

Потому что они думают, что
По ссылке: https://github.com/facebook/react/issues/11527#issuecomment-360199710

... мы согласны с тем, что setState() повторный рендеринг синхронно во многих случаях будет неэффективным

Асинхронный setState() делает жизнь очень трудной для тех, кто только начинает, и даже переживает, к сожалению:
- неожиданные проблемы рендеринга: задержка рендеринга или отсутствие рендеринга (на основе логики программы)
- передача параметров имеет большое значение
среди других вопросов.

Вот пример, который помог:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make 'this' work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Надеюсь, это поможет.