javascript - 如果在该函数之外设置状态,则 React JS 高阶组件不执行函数
问题描述
我想不出问题标题的正确句子,所以首先我想道歉,如果标题有点令人困惑。
所以我刚刚完成了 React JS 的学习并尝试构建一个应用程序进行练习。在这个应用程序中,有一个登录表单,如果输入的电子邮件或密码错误,它将显示警报并空白字段。
使用基本的 React JS 实现一切都很好,但是当我尝试修改我的代码以实现高阶组件时,应用程序变得有问题,我似乎无法找到罪魁祸首。
所以问题是,如果我输入的电子邮件或密码不正确,然后单击登录,那么这些字段将为我输入/重试,但似乎这些字段不会接受任何输入,无论如何它都保持空白我在键盘上按了哪些键,所以我需要刷新页面,以便字段可以再次接受我的输入。
这是我的代码:
function ResponseAlert(props){
if(props.alertStatus!==undefined && props.alertStatus!==null){
const className = 'alert alert-' + props.alertStatus;
return <div className={className}>{props.alertMessage}</div>
}
return <div></div>;
}
function H2Title(props) {
return <h2>{props.title}</h2>;
}
class InputText extends React.Component{
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(evt){
this.props.onInputChange(evt.target.id, evt.target.value);
}
render(){
return (
<div className="form-group">
<label htmlFor={this.props.elemId}>{this.props.label}</label>
<input type={this.props.type} className="form-control" name={this.props.elemId} id={this.props.elemId} value={this.props.elemValue} onChange={this.handleChange} required={this.props.required} />
</div>
);
}
}
function withSetStateFormData(WrappedComponents, componentName){
return class extends React.Component{
constructor(props){
super(props);
this.state = { formdata: { email: '', password: '' } };
this.setStateFormData = this.setStateFormData.bind(this);
}
setStateFormData(field, value){
this.setState(function(state, props){
let tempData = state.formdata;
tempData[field] = value;
return {
formdata: tempData
};
});
}
render(){
return <WrappedComponents handleInputChange={this.setStateFormData} {...this.props} {...this.state} />
}
}
}
class FormLogin extends React.Component {
constructor(props){
super(props);
this.state = {
alertstatus: null,
alertmessage: '',
formdata: this.props.formdata
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(evt){
evt.preventDefault();
const self = this;
fetch("http://someurl.com/login.php",
{
mode: 'cors',
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(this.state.formdata)
})
.then(function (response) {
if (response.status === 200) {
response.json().then(function (resData) {
alert('Yeay! Successful login.');
});
}
else{
//here is where the problem lies, I think.
self.setState({
alertstatus: 'danger',
alertmessage: 'Login failed. Email or password is incorrect.',
formdata: { email: '', password: '' }
});
}
})
.catch(function (err) {
console.log(err);
});
return false;
}
render(){
const handleInputChange = this.props.handleInputChange;
return (
<div>
<ResponseAlert alertStatus={this.state.alertstatus} alertMessage={this.state.alertmessage} />
<H2Title title="Login"/>
<form onSubmit={this.handleSubmit}>
<InputText type="email" elemId="email" label="Email Address" elemValue={this.state.formdata.email} onInputChange={handleInputChange} required={true}/>
<InputText type="password" elemId="password" label="Password" elemValue={this.state.formdata.password} onInputChange={handleInputChange} required={true}/>
<button type='submit' className='btn btn-primary'>Login</button>
</form>
</div>
);
}
}
const EnhancedComponent = withSetStateFormData(FormLogin, "login");
ReactDOM.render(<EnhancedComponent />, document.querySelector('#app'));
所以我认为在执行 setState 函数以清空电子邮件和密码后,onChange 事件以某种方式拒绝更新输入更改的字段。
任何人都请告诉我是什么导致了这种行为?
解决方案
我只想说您不需要在这里使用更高阶的组件,但是您尝试做的事情可以工作。
我看到了几个问题。
首先,您正在InputText
value
根据FormLogin
状态进行设置。没关系,但是,当InputText
更改时,它会调用从高阶组件onInputChange
设置为的。setStateFormData
问题是它setStateFormData
设置了高阶组件的状态,而不是FormLogin
. 所以结果是FormLogin
状态没有更新,这意味着value
forInputText
没有更新。
我知道您在想什么,“但是在我单击提交之前输入确实会更新”。是的,没有。
这给我们带来了与 JS 引用有关的第二个问题。
来自 React 文档
state
是对应用更改时的组件状态的引用。它不应该直接突变。相反,应该通过基于来自state
和的输入构建一个新对象来表示更改props
。
如果我们看到setStateFormData
您在技术上是state
通过首先在state
with中引用一个对象let tempData = state.formdata;
然后用 修改引用的对象来直接改变tempData[field] = value;
。这在 React 中是一个很大的禁忌,因为它会导致各种令人困惑的错误,比如这个。
您可以从旧状态构建新状态,但您只能复制值,不能复制引用。setStateFormData
我们可以通过如下重写来解决这个问题:
setStateFormData(field, value){
this.setState(function(state, props){
let newFormData = {
email: state.formdata.email;
password: state.formdate.password;
};
newFormData[field] = value;
return {
formdata: newFormData
};
});
}
现在,我们正在formdata
从旧状态进行深度复制。太好了,一个错误。如果我们再次测试应用程序,现在我们看到输入在提交之前或之后从未更新。这是因为上面提到的第一个问题。
之前,InputText
value
由于FormLogin
状态似乎正在更新,所以 in 似乎正在更新,但state.formdata
inFormLogin
只是在withstate.formdata
的构造函数中设置的高阶组件中的引用。这意味着当直接改变高阶组件的状态时,它也直接改变了 的状态。这使它看起来一切正常,但实际上只是因为引用。一旦引用丢失,应用程序就会崩溃。那么,我们什么时候失去了参考?如您所料,当我们调用.FormLogin
formdata: this.props.formdata
setStateFormData
FormLogin
setState
handleSubmit
self.setState({
alertstatus: 'danger',
alertmessage: 'Login failed. Email or password is incorrect.',
formdata: { email: '', password: '' }
});
这分配给一个新对象,打破了高阶组件中state.formdata
的引用。FormLogin
state.formdata
好的,那么如何解决它。有几种方法。
我建议formdata
从完全状态中删除FormLogin
。这样您就不必担心保持两个formdata
对象同步。clearFormData
只需在高阶组件上创建一个函数并将其传递FormLogin
给 call in handleSubmit
。另外,您需要TextInput
value
根据FormLogin
. 我认为这是最干净的解决方案,同时仍然使用高阶组件。
最简单的解决方案当然是摆脱高阶组件,但如果您的目标是专门了解高阶组件,我不确定这是否是一种选择。
最后,您可以实现一个getDerivedStateFromProps函数,每次它的 props 更改时都会在FormLogin
该函数上重建FormLogin
状态,而这种情况恰好在每次高阶状态更改时发生。formdata
这是一种在FormLogin
高阶组件中发生变化时更新所有内容的干净方法。问题是您仍然需要担心formdata
每次更改高阶组件时都会更新FormLogin
.
我希望这有帮助。
推荐阅读
- css - 防止旋转木马内的组件调整大小
- react-native - base64 的图像文件路径在 iOS 上不起作用(React-Native)
- postgresql - 锁定表或行以检查条件并插入行
- php - 按信息订购尚未批准或拒绝
- python - 如何使用 python3 将 wiki-link 图像转换为普通的 md-link 图像?
- jquery - 我们如何在一个地方定义默认配置值并将其用于应用程序中的所有数据表
- javascript - 如何限制用户在日期字段中手动输入日期,用户只能在给定的日期范围内输入日期
- javascript - 在嵌套函数上调用方法时出现 TypeError
- html - 单选按钮不检查标签内
- angular - 绑定值的角度错误。元素隐含地具有“任何”类型。“对象”类型上不存在属性“标题”.ts(7053)