为什么调用react setState方法不立即改变状态?

我正在阅读文档的形式部分,只是尝试了这段代码来演示onChange的用法(JSBIN)。

var React= require('react');


var ControlledForm= React.createClass({
getInitialState: function() {
return {
value: "initial value"
};
},


handleChange: function(event) {
console.log(this.state.value);
this.setState({value: event.target.value});
console.log(this.state.value);


},


render: function() {
return (
<input type="text" value={this.state.value} onChange={this.handleChange}/>
);
}
});


React.render(
<ControlledForm/>,
document.getElementById('mount')
);

当我在浏览器中更新<input/>值时,handleChange回调中的第二个console.log打印了与第一个console.log相同的value,为什么我不能在handleChange回调的范围内看到this.setState({value: event.target.value})的结果?

234304 次浏览

来自React的文档:

setState()不会立即改变this.state,而是创建一个 挂起状态转换。调用此函数后访问this.state 方法可能返回现有的值。没有 保证对setState的调用同步操作,并且调用可以

如果希望在状态改变发生后执行函数,可以将其作为回调传入。

this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});

正如React文档中提到的,不能保证setState被同步触发,所以你的console.log可能会在更新之前返回状态。

Michael Parker提到在setState中传递回调。另一种处理状态改变后逻辑的方法是通过componentDidUpdate生命周期方法,这是React文档中推荐的方法。

通常,我们建议对这种逻辑使用componentDidUpdate()。

当可能连续触发__abc0,并且您希望在每次状态更改后触发相同的函数时,这特别有用。与其为每个setState添加回调函数,不如将函数放在componentDidUpdate中,必要时在其中添加特定的逻辑。

// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}

注意react生命周期方法!

我工作了几个小时才发现getDerivedStateFromProps将在每个setState()之后被调用。

😂

您可以尝试使用ES7 async/await。举个例子:

handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}

async-await语法完美地适用于以下内容…

changeStateFunction = () => {
// Some Worker..


this.setState((prevState) => ({
year: funcHandleYear(),
month: funcHandleMonth()
}));


goNextMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}


goPrevMonth = async () => {
await this.changeStateFunction();
const history = createBrowserHistory();
history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

简单地说-这个。setState({data: value})是异步的 这意味着它会移出调用堆栈,只会返回

请阅读事件循环,以清楚地了解JS中的异步性质,以及为什么需要时间来更新-

https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

因此,

    this.setState({data:value});
console.log(this.state.data); // will give undefined or unupdated value

,因为它需要时间更新。 实现上述过程-

    this.setState({data:value},function () {
console.log(this.state.data);
});

由于setState的异步性质,在调用setState方法后访问this.state并不保证返回更新后的状态。

为了保证调用setState后更新,有两个解决方案。

就像上面答案中的提到一样,把你的代码放在componentDidUpdate方法中

如上面的答案的另一个中所提到的,将你的东西作为回调传递

 this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});

需要注意的是,这两种解决方案并不能明显互换。一个不能轻易地解决另一个的所有用例。作为一般规则,如果可以的话,最佳实践建议首选解决方案1。但是,在一些用例中,只有解决方案2“更有效”;比如“更新我的视图并发送我的数据”;用例。这个用例是这样的:

在添加一个项目后,例如,“Add schedule &”,我想这两个将该项目添加到前端列表,并立即将刚刚更新的列表发布到后端,如下面的概念所示: enter image description here < / p >

如果你不做任何解决方案,也就是说,如果你只在你的代码中这样说:

addToItemArray = () => {
this.setState\{\{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}


<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>

... 你将发布列表不包括 "送货到Max"item,因为当你this.postData()时状态不会更新(同样,因为它是异步的)。

如果您使用解决方案1,您将在输入日程名称文本框中的每个字符后进行POST !

还有其他方法来满足这个用例,但是解决方案2在阅读代码时最好地传达了意图。

鉴于这个用例在几乎每个web应用程序中都无处不在,迈克尔的回答解释的回调技术是每个开发人员工具包中不可或缺的一段代码。

React会对不同的set状态调用进行洗浴,这样它就可以确定重新呈现网站的最优策略。

假设您有一个应用程序,其中有许多不同的组件。也许,通过单击一个按钮,您正在更新多个组件的状态,而不仅仅是当前组件。在这种情况下,React并不想完全隔离,而是独立地执行所有这些不同的更新。

React想要弄清楚它是否可以将所有这些更新堆叠在一起,也许有一种更优的更新这些组件的方式,这样它就更性能。这就是React在幕后所做的事情。因此,set state调用是异步调用。

有时state会出现此问题。
对于钩子,你应该使用useEffect钩子,如下所示-

const [fruit, setFruit] = useState('');
 

setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])

这救了我的一天,希望会帮助你!!