我正在阅读 reactjs 文档的 Forms 部分,并尝试使用此代码来演示 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
回调的范围?
来自 React 的 documentation:
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() 来代替这种逻辑。
当可能有连续的 setState
被触发并且您希望在每次状态更改后触发相同的函数时,这尤其有用。您可以将函数放在 componentDidUpdate
内,而不是向每个 setState
添加回调,必要时在其中包含特定逻辑。
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
你可以尝试使用 ES7 async/await。例如使用您的示例:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
注意 react 生命周期方法!
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
https://reactjs.org/docs/react-component.html
我工作了几个小时才发现在每个 setState()
之后都会调用 getDerivedStateFromProps
。
😂
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
的异步特性,在调用 setState
方法后访问 this.state
不能保证返回更新的状态。
为保证调用 setState
后更新,您可以采用两种解决方案。
解决方案 1: 作为上述答案之一中的 mentioned,将您的代码放入 componentDidUpdate
方法中
解决方案 2: 如上述答案的 another 中所述,将您的内容作为回调传递
this.setState({value: myValue}, function () {
this.functionThatIsExecutedWhenStateIsUpdated();
});
需要注意的是,这两种解决方案显然不能互换。一个不能轻易解决另一个的所有用例。作为一般规则,如果可以,最佳实践表明解决方案 1 是首选。但是,在某些用例中,只有解决方案 2“更有效”有效,例如“update-my-view-and-post-my-data”用例。这个用例是这样的:
https://i.stack.imgur.com/hktFa.png
如果你不做任何一个解决方案,即如果你只在你的代码中这样说:
addToItemArray = () => {
this.setState{{ scheduledItemsArray: newObjectListWithMax}}
this.postData();
}
<button className="btn btn-secondary btn-block" onClick={this.addToItemArray}>Add Shedule</button>
...您将发布列表排除“Delivery to Max”项目,因为当您this.postData()
时状态不会更新(再次,因为它是异步的)。
如果您使用解决方案 1,您将在 Schedule Name 文本框中输入每个字符后进行 POST!
还有其他方法可以满足此用例,但解决方案 2 在阅读代码时最能传达意图。
鉴于此用例在几乎每个 Web 应用程序中无处不在,Michael's answer 所解释的回调技术是每个开发人员工具包中不可或缺的一段代码。
有时状态会出现此问题。
如果是钩子,您应该使用 useEffect
钩子,如下所示 -
const [fruit, setFruit] = useState('');
setFruit('Apple');
useEffect(() => {
console.log('Fruit', fruit);
}, [fruit])
这拯救了我的一天,希望能帮助你!!!
简单地说 - this.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);
});
React 处理不同的设置状态调用,以便它可以确定重新呈现网站的最佳策略是什么。
想象一下,您有一个应用程序,其中包含许多不同的组件。也许,只需单击一个按钮,您就可以更新多个组件的状态,而不仅仅是当前组件。在这种情况下,React 不希望完全隔离并独立完成所有这些不同的更新。
React 想弄清楚它是否可以将所有这些更新堆叠在一起,也许有一种更优化的方式来更新这些组件,从而提高性能。这就是 React 在幕后所做的。因此,设置状态调用是异步调用。
不定期副业成功案例分享
componentDidUpdate
。状态改变后会被调用。shouldComponentUpdate
中另有指定行为。在您传递给setState
的回调中,您到底想要在重新渲染之前发生什么?