我正在研究todo应用程序。这是违规代码的一个非常简化的版本。我有一个复选框:
<p><input type="checkbox" name="area" checked={this.state.Pencil} onChange={this.checkPencil}/> Writing Item </p>
这是调用复选框的函数:
checkPencil(){
this.setState({
pencil:!this.state.pencil,
});
this.props.updateItem(this.state);
}
updateItem是一个映射到dispatch to redux的函数
function mapDispatchToProps(dispatch){
return bindActionCreators({ updateItem}, dispatch);
}
我的问题是,当我调用updateItem action和console.log状态时,它总是落后一步。如果取消选中该复选框而不是true,我仍然会将true状态传递给updateItem函数。我是否需要调用另一个函数来强制状态更新?
答案 0 :(得分:72)
您应该调用第二个函数作为setState的回调,因为setState是异步发生的。类似的东西:
this.setState({pencil:!this.state.pencil}, myFunction)
但是在你的情况下,因为你想要用参数调用这个函数,你需要更多的创造性,并且可能创建你自己的函数来调用道具中的函数:
myFunction = () => {
this.props.updateItem(this.state)
}
将它们组合在一起就可以了。
答案 1 :(得分:14)
由于各种原因(主要是性能),在React中调用setState()
是异步的。在封面下,React会将对setState()
的多次调用批量处理为单个状态变异,然后重新渲染组件一次,而不是为每次状态更改重新渲染。
幸运的是,解决方案非常简单 - setState
接受回调参数:
checkPencil(){
this.setState({
pencil:!this.state.pencil,
}, function () {
this.props.updateItem(this.state);
}.bind(this));
}
答案 2 :(得分:9)
当您使用当前状态的属性更新状态时,React文档建议您使用setState
的函数调用版本而不是对象。
所以setState((state, props) => {...})
代替setState(object)
。
原因是setState
更多的是要求国家改变而不是立即改变。 React批量调用setState
次调用以提高性能。
意味着您正在检查的状态属性可能不稳定。 这是一个值得注意的潜在陷阱。
有关详细信息,请参阅此处的文档:https://facebook.github.io/react/docs/react-component.html#setstate
要回答你的问题,我会这样做。
checkPencil(){
this.setState((prevState) => {
return {
pencil: !prevState.pencil
};
}, () => {
this.props.updateItem(this.state)
});
}
答案 3 :(得分:8)
这是因为它发生了异步,所以意味着在那个时候可能还没有更新......
根据React v.16文档,您需要使用第二种形式的setState()
接受函数而不是对象:
状态更新可能是异步的
React可以将多个setState()调用批处理为单个更新 性能
因为this.props和this.state可以异步更新,所以 不应该依赖它们的值来计算下一个状态。
例如,此代码可能无法更新计数器:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
要修复它,请使用接受函数的第二种形式的setState() 而不是一个对象。该功能将接收先前的状态 作为第一个参数,以及应用更新时的道具 作为第二个论点:
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
答案 4 :(得分:2)
我使用了rossipedia和Ben Hare的建议并做了以下事情:
checkPencil(){
this.setState({
pencil:!this.state.pencil,
}, this.updatingItem);
}
updatingItem(){
this.props.updateItem(this.state)
}
答案 5 :(得分:2)
Ben对如何解决眼前问题有很好的答案,但我也建议避免重复状态
如果状态在redux中,则您的复选框应该从prop或store读取其自己的状态,而不是在其自己的组件和全局存储中跟踪检查状态
做这样的事情:
<p>
<input
type="checkbox"
name="area" checked={this.props.isChecked}
onChange={this.props.onChange}
/>
Writing Item
</p>
一般规则是,如果你发现在多个地方需要一个州,将它提升到一个共同的父母(并不总是减少),以保持只有一个单一的真相来源
答案 6 :(得分:2)
试试这个
this.setState({inputvalue: e.target.value}, function () {
console.log(this.state.inputvalue);
this.showInputError(inputs[0].name);
});
showInputError函数,如果使用任何表单
进行验证答案 7 :(得分:2)
在 Ben Hare 的回答中,如果有人想使用 React Hooks 实现相同的目标,我在下面添加了示例代码。
import React, { useState, useEffect } from "react"
let [myArr, setMyArr] = useState([1, 2, 3, 4]) // the state on update of which we want to call some function
const someAction = () => {
let arr = [...myArr]
arr.push(5) // perform State update
setMyArr(arr) // set new state
}
useEffect(() => { // this hook will get called everytime when myArr has changed
// perform some action which will get fired everytime when myArr gets updated
console.log('Updated State', myArr)
}, [myArr])
答案 8 :(得分:1)
首先设定你的价值。继续你的工作之后。
this.setState({inputvalue: e.target.value}, function () {
this._handleSubmit();
});
_handleSubmit() {
console.log(this.state.inputvalue);
//Do your action
}
答案 9 :(得分:1)
如上所述,setState()
本质上是异步的。我只需使用async
await
就解决了这个问题。
以下是引用示例:
continue = async (e) => {
e.preventDefault();
const { values } = this.props;
await this.setState({
errors: {}
});
const emailValidationRegex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
if(!emailValidationRegex.test(values.email)){
await this.setState((state) => ({
errors: {
...state.errors,
email: "enter a valid email"
}
}));
}
}
答案 10 :(得分:0)
您也可以像下面那样更新状态两次并立即更新状态,这对我有用:
this.setState(
({ app_id }) => ({
app_id: 2
}), () => {
this.setState(({ app_id }) => ({
app_id: 2
}))
} )