反应形式为 Change-> setState

我遇到了这个问题,建立一个网络应用程序,我复制了它在这个 Jsfiddle。从本质上讲,我希望每次向输入内容时都有一个输入调用 this.setState({message: input_val}),然后将其传递到父 App 类中,该类随后将消息重新呈现到 Message 类中。然而,输出似乎总是落后于我实际输入的内容一步。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')
);
109557 次浏览

setState的调用不是同步的。它创建了一个“挂起状态转换”(详情请参阅 给你)。您应该显式地传递新的 input值作为引发事件的一部分(类似于您的示例中的 handleSubmit)。

参见 这个示例。

handleSubmit: function(txt) {
this.props.onChange(txt);
},
handleChange: function(e) {
var value = e.target.value;
this.setState({message: value});
this.handleSubmit(value);
},

MyForm 没有理由在这里使用 state。另外,将 onChange 放在表单上而不是您感兴趣的输入是很奇怪的。受控组件应该是首选的,因为它们的行为更加明显,而且任何时候应用程序的消息状态改变(即使你以后允许消息改变它) ,它在任何地方都是正确的。

这也使您的代码更短一些,相当简单。

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>
);
}
});

Jsbin

我发现定义3个处理程序函数来获取组件状态的一些值对我来说非常麻烦,所以我决定根本不使用 state。我刚刚为存储所需值的组件定义了一个附加属性。

所以我最终得到了这样一个代码:

//...
},
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的语法,请检查此 链接

因为这个原因,我把头发揪了大概一个小时,所以我决定和大家分享... ... 如果你的回调仍然落后一步,而且看起来不起作用,确保你没有使用带括号的 打电话函数... ... 只要把它传进去就行了。菜鸟错误。

右:

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 的 异步行为,所以我用 异步等待解决了这个问题

onChangeEmail=async x =>{
await this.setState({email:x})
}

使用 setState hook

useEffect(() => {
your code...
}, [yourState]);

您可以像其他人提到的那样,将基于类的组件重构为功能组件。缺点是,这可能相当耗时,具体取决于需要重构的代码行数,而且很容易出错。

我将使用您的 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,而不是 down... ..。

上面提到了一些解决方案,其中一些存在一些问题,但是 Ajsaule 是正确答案:

    // solve the problem: setstate always one step behind
useEffect(() => { isFormValid(fields, setFieldsError) }, [fields])
const handleChange = (field: string ) => (evt: React.ChangeEvent<HTMLInputElement>) => {
setFields({ ...fields, [field]: evt.target.value })
}




const isFormValid = (fields: FieldsState, setFieldsError: React.Dispatch<React.SetStateAction<TempObj>>) => {
const tempObj: TempObj = {}
const { email, password } = fields


if( !isEmail(email) ) tempObj.email = 'Invalid Email Address'
if( password.length < 8 ) tempObj.password = 'password must by atleast 8 character long'


Object.keys(fields).forEach(field => {
if(!fields[field as keyof FieldsState]) tempObj[field] = `'${field}' is emapty`
})
setFieldsError(tempObj)


return Object.values(tempObj).every( item => item == '' )
}