Почему setState в реале Async вместо Sync?
Я только что обнаружил, что в реакции this.setState()
функция в любом компоненте является асинхронной или вызывается после завершения функции, в которой она была вызвана.
Теперь я искал и нашел этот блог (setState() Операция мутации состояния может быть синхронной в ReactJS)
Здесь он обнаружил, что setState
является асинхронным (вызывается, когда стек пуст) или синхронизируется (вызывается сразу после вызова), в зависимости от того, как было инициировано изменение состояния.
Теперь эти две вещи трудно переварить.
- В блоге функция
setState
вызывается внутри функции updateState
, но то, что вызвало функцию updateState
, не является тем, о чем должна знать вызываемая функция.
- Почему они делают
setState
асинхронным, поскольку JS является однопоточным языком, и этот setState не является вызовом WebAPI или сервера, поэтому должен выполняться только в потоке JS. Делают ли они это так, чтобы повторный рендеринг не останавливал всех слушателей событий и прочее, или есть какая-то другая проблема дизайна.
Ответы
Ответ 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) {
// ..
}
Надеюсь, это поможет.