反应形式 onChange->setState 落后一步
React form onChange->setState one step behind
我在构建 webapp 时遇到了这个问题,我在这个 jsfiddle 中复制了它。本质上,我希望每次输入内容时调用 this.setState({message: input_val})
的输入,然后将其传递给父应用程序 class,然后将消息重新呈现到消息 class 上。然而,输出似乎总是比我实际键入的内容晚一步。 jsfiddle 演示应该是不言自明的。我想知道我是否做错了提示。
html
<script src="http://facebook.github.io/react/js/jsfiddle-integration.js"></script>
<div id='app'></div>
js
var App = React.createClass({
getInitialState: function() {
return {message: ''}
},
appHandleSubmit: function(state) {
this.setState({message: state.message});
console.log(this.state.message);
},
render: function() {
return (
<div className='myApp'>
<MyForm onChange={this.appHandleSubmit}/>
<Message message={this.state.message}/>
</div>
);
}
});
var MyForm = React.createClass({
handleSubmit: function() {
this.props.onChange(this.state);
},
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value});
this.handleSubmit();
},
render: function() {
return (
<form className="reactForm" onChange={this.handleChange}>
<input type='text' />
</form>
);
}
});
var Message = React.createClass({
render: function() {
return (
<div className="message">
<p>{this.props.message}</p>
</div>
)
}
});
React.render(
<App />,
document.getElementById('app')
);
对 setState
的调用不是同步的。它创建一个 "pending state transition."(有关详细信息,请参阅 here)。您应该明确传递新的 input
值作为引发事件的一部分(例如 handleSubmit
在您的示例中)。
参见 this 示例。
handleSubmit: function(txt) {
this.props.onChange(txt);
},
handleChange: function(e) {
var value = e.target.value;
this.setState({message: value});
this.handleSubmit(value);
},
MyForm 没有理由在这里使用状态。将 onChange 放在表单上而不是您感兴趣的输入也很奇怪。受控组件应该是首选,因为它们的行为更明显,并且任何时候 App 的消息状态改变(即使你例如允许 Message 稍后改变它),它在任何地方都是正确的。
这也会使您的代码更短一些,并且简单得多。
var App = React.createClass({
getInitialState: function() {
return {message: ''}
},
appHandleSubmit: function(message) {
this.setState({message: message});
},
render: function() {
return (
<div className='myApp'>
<MyForm onChange={this.appHandleSubmit}
message={this.state.message} />
<Message message={this.state.message}/>
</div>
);
}
});
var MyForm = React.createClass({
handleInputChange: function(e){
this.props.onChange(e.target.value);
},
// now always in sync with the parent's state
render: function() {
return (
<form className="reactForm">
<input type='text' onChange={this.handleInputChange}
value={this.props.message} />
</form>
);
}
});
我发现定义 3 个处理程序函数只是为了获取组件状态的一些值对我来说非常麻烦,所以我决定根本不使用状态。我刚刚为存储所需值的组件定义了一个额外的 属性。
所以我最终得到了一个看起来像这样的代码:
//...
},
text: '',
handleChange: function(event) {
this.text = event.target.value;
this.forceUpdate();
},
render: function() {
return <div>
<InputComponent onChange={this.handleChange}/>
<DisplayComponent textToDisplay={this.text}/>
</div>
}
//...
有一个更简单的方法可以做到这一点,setState(updater, callback)
是一个异步函数,它将回调作为第二个参数,
只需将 handleSubmit 作为回调传递给 setState 方法,这种方式仅在 setState 完成后 handleSubmit 将被执行。
例如
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit);
}
尝试像上面那样更改 handleChange() 方法,它将起作用。
对于使用 setState 的语法,检查这个 link
因为这个,我焦急了一个小时,所以我决定分享...如果你的回调仍然落后一步并且似乎不起作用,请确保你不要 CALL 带括号的函数... 传入即可。菜鸟错误。
右:
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit);
}
VS
错误:
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit());
}
知道问题在于没有 setState 的 异步行为 我用 async await
解决了我的问题
onChangeEmail=async x =>{
await this.setState({email:x})
}
使用 setState 挂钩
useEffect(() => {
your code...
}, [yourState]);
您可以将基于 class 的组件重构为其他人提到的功能组件。缺点是这可能会非常耗时,具体取决于您必须重构多少行代码并且很可能容易出错。
我将使用您的 changeHandler
示例来展示如何在功能组件中完成它。
const INITIAL_DATA = {
message: ""
}
const [form, setForm] = useState({...INITIAL_DATA})
const changeHandler = (e) = setForm({
...form,
[e.target.name]: e.target.value
})
<InputField name={message} value={form.message} onChange={changeHandler}>
^ 上面的代码将产生您解释的 onChange 落后 setState
一步的行为。正如其他人所说,这是由于 setState
钩子的异步性质造成的。
解决这个问题的方法是使用 useEffect
钩子,它允许你引入状态依赖。因此,当 setState
完成其更新周期时, useEffect
将触发。所以将下面的代码添加到上面的代码和 BAM 中。它应该显示状态变化而没有步骤延迟。
useEffect(() => {
doSomeValidationHere()
orSendOffTheForm()
}, [form])
*注意我们如何在 useEffect(() => {})
钩子之后添加一个依赖数组。
额外信息:
- 如果依赖数组为空,它只会 运行 在组件挂载和第一次渲染时
- 如果没有数组,每次页面渲染都会运行
- 如果数组中有一个状态名称,则每次该状态完成设置时都会运行
或者像我的情况一样 - 只使用 onKeyUp,而不是向下...
我在构建 webapp 时遇到了这个问题,我在这个 jsfiddle 中复制了它。本质上,我希望每次输入内容时调用 this.setState({message: input_val})
的输入,然后将其传递给父应用程序 class,然后将消息重新呈现到消息 class 上。然而,输出似乎总是比我实际键入的内容晚一步。 jsfiddle 演示应该是不言自明的。我想知道我是否做错了提示。
html
<script src="http://facebook.github.io/react/js/jsfiddle-integration.js"></script>
<div id='app'></div>
js
var App = React.createClass({
getInitialState: function() {
return {message: ''}
},
appHandleSubmit: function(state) {
this.setState({message: state.message});
console.log(this.state.message);
},
render: function() {
return (
<div className='myApp'>
<MyForm onChange={this.appHandleSubmit}/>
<Message message={this.state.message}/>
</div>
);
}
});
var MyForm = React.createClass({
handleSubmit: function() {
this.props.onChange(this.state);
},
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value});
this.handleSubmit();
},
render: function() {
return (
<form className="reactForm" onChange={this.handleChange}>
<input type='text' />
</form>
);
}
});
var Message = React.createClass({
render: function() {
return (
<div className="message">
<p>{this.props.message}</p>
</div>
)
}
});
React.render(
<App />,
document.getElementById('app')
);
对 setState
的调用不是同步的。它创建一个 "pending state transition."(有关详细信息,请参阅 here)。您应该明确传递新的 input
值作为引发事件的一部分(例如 handleSubmit
在您的示例中)。
参见 this 示例。
handleSubmit: function(txt) {
this.props.onChange(txt);
},
handleChange: function(e) {
var value = e.target.value;
this.setState({message: value});
this.handleSubmit(value);
},
MyForm 没有理由在这里使用状态。将 onChange 放在表单上而不是您感兴趣的输入也很奇怪。受控组件应该是首选,因为它们的行为更明显,并且任何时候 App 的消息状态改变(即使你例如允许 Message 稍后改变它),它在任何地方都是正确的。
这也会使您的代码更短一些,并且简单得多。
var App = React.createClass({
getInitialState: function() {
return {message: ''}
},
appHandleSubmit: function(message) {
this.setState({message: message});
},
render: function() {
return (
<div className='myApp'>
<MyForm onChange={this.appHandleSubmit}
message={this.state.message} />
<Message message={this.state.message}/>
</div>
);
}
});
var MyForm = React.createClass({
handleInputChange: function(e){
this.props.onChange(e.target.value);
},
// now always in sync with the parent's state
render: function() {
return (
<form className="reactForm">
<input type='text' onChange={this.handleInputChange}
value={this.props.message} />
</form>
);
}
});
我发现定义 3 个处理程序函数只是为了获取组件状态的一些值对我来说非常麻烦,所以我决定根本不使用状态。我刚刚为存储所需值的组件定义了一个额外的 属性。
所以我最终得到了一个看起来像这样的代码:
//...
},
text: '',
handleChange: function(event) {
this.text = event.target.value;
this.forceUpdate();
},
render: function() {
return <div>
<InputComponent onChange={this.handleChange}/>
<DisplayComponent textToDisplay={this.text}/>
</div>
}
//...
有一个更简单的方法可以做到这一点,setState(updater, callback)
是一个异步函数,它将回调作为第二个参数,
只需将 handleSubmit 作为回调传递给 setState 方法,这种方式仅在 setState 完成后 handleSubmit 将被执行。
例如
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit);
}
尝试像上面那样更改 handleChange() 方法,它将起作用。
对于使用 setState 的语法,检查这个 link
因为这个,我焦急了一个小时,所以我决定分享...如果你的回调仍然落后一步并且似乎不起作用,请确保你不要 CALL 带括号的函数... 传入即可。菜鸟错误。
右:
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit);
}
VS
错误:
handleChange: function(e) {
console.log(e.target.value);
this.setState({message: e.target.value}, this.handleSubmit());
}
知道问题在于没有 setState 的 异步行为 我用 async await
解决了我的问题onChangeEmail=async x =>{
await this.setState({email:x})
}
使用 setState 挂钩
useEffect(() => {
your code...
}, [yourState]);
您可以将基于 class 的组件重构为其他人提到的功能组件。缺点是这可能会非常耗时,具体取决于您必须重构多少行代码并且很可能容易出错。
我将使用您的 changeHandler
示例来展示如何在功能组件中完成它。
const INITIAL_DATA = {
message: ""
}
const [form, setForm] = useState({...INITIAL_DATA})
const changeHandler = (e) = setForm({
...form,
[e.target.name]: e.target.value
})
<InputField name={message} value={form.message} onChange={changeHandler}>
^ 上面的代码将产生您解释的 onChange 落后 setState
一步的行为。正如其他人所说,这是由于 setState
钩子的异步性质造成的。
解决这个问题的方法是使用 useEffect
钩子,它允许你引入状态依赖。因此,当 setState
完成其更新周期时, useEffect
将触发。所以将下面的代码添加到上面的代码和 BAM 中。它应该显示状态变化而没有步骤延迟。
useEffect(() => {
doSomeValidationHere()
orSendOffTheForm()
}, [form])
*注意我们如何在 useEffect(() => {})
钩子之后添加一个依赖数组。
额外信息:
- 如果依赖数组为空,它只会 运行 在组件挂载和第一次渲染时
- 如果没有数组,每次页面渲染都会运行
- 如果数组中有一个状态名称,则每次该状态完成设置时都会运行
或者像我的情况一样 - 只使用 onKeyUp,而不是向下...