Почему setState в Reactjs Async вместо Sync?


127

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

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

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

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

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


1
Я написал статью сегодня , что помогает описать немного климата вокруг setState: medium.com/@agm1984/...
agm1984

Ответы:


157

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

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

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

Вместо того:

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

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

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

18
Да, все в порядке, у нас есть функция callBack, которую мы можем использовать, но вопрос не в этом.
Anup

12
Надеюсь, это поможет кому-то другому, как наткнуться на этот вопрос.
JoeTidee

2
да, это может быть полезно
Anup

97

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

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


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

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

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


59
Если вам нужно обеспечить порядок событий после вызова setState, вы можете передать функцию обратного вызова. this.setState({ something: true }, () => console.log(this.state))
ianks

1
Спасибо @Sachin за объяснение. Однако я все еще сомневаюсь, может ли это быть синхронным, как объясняется в блоге?
Аджай Гаур

2
Еще одно глупое дизайнерское решение в React. Сделайте обновление состояния синхронным, а рендеринг - асинхронным. Вы можете группировать рендеринг, но я хочу иметь возможность устанавливать что-то столь же примитивное, как переменные состояния, без необходимости иметь дело с условиями гонки.
ig-dev

Почему бы не разрешить установку опции, чтобы сделать функцию асинхронной или синхронной? Это была бы полезная функция
Randall Coding

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

16

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

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

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


Я не пометил ни одного ответчика как правильный. Люди пишут, как это обойти. Не ответ на заданный вопрос. эта статья кажется хорошей.
Anup

@Anup Ответ немного сложнее, чем просто «асинхронный» или «синхронный». Его всегда следует рассматривать как «асинхронный», но в некоторых случаях он может действовать как «синхронный». Надеюсь, я пролил для вас свет.
gillyb

8

Хорошая статья здесь 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



1

Представьте, что в каком-то компоненте увеличивается счетчик:

  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');
    }

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

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


0

Да, 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) {
    // ..
}

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.