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

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

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

Эти две вещи сложно переварить

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

Ответы (8)

Вы можете использовать следующую упаковку для вызова синхронизации

this.setState ((state => {
  возвращение{
    что-то
  }
})

setState является асинхронным. Вы можете увидеть в этой документации Reactjs

React намеренно «ждет», пока все компоненты не вызовут setState () в своих обработчиках событий, прежде чем начать повторный рендеринг. Это повышает производительность, избегая ненужных повторных рендеров.

Однако вы все еще можете задаться вопросом, почему React не обновляет this.state сразу без повторного рендеринга.

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

Вы по-прежнему можете выполнять функции, если это зависит от изменения значения состояния:

Вариант 1: Использование функции обратного вызова с setState

this.setState({
   value: newValue
},()=>{
   // It is an callback function.
   // Here you can access the update value
   console.log(this.state.value)
})

Вариант 2: с использованием componentDidUpdate Эта функция будет вызываться всякий раз, когда изменяется состояние этого конкретного класса.

componentDidUpdate(prevProps, prevState){
    //Here you can check if value of your desired variable is same or not.
    if(this.state.value !== prevState.value){
        // this part will execute if your desired variable updates
    }
}

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

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

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

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

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

Хорошая статья 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 ({.....}, обратный вызов)

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

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

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

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

Вместо:

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

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

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

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

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


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

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

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

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

  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
        
// a child button
... ) } }

К родительскому и дочернему компонентам прикреплен обработчик счетчика. Это сделано специально, чтобы мы могли дважды выполнить 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 в очереди, передавая предыдущее состояние компонента.

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

2022 WebDevInsider