我想不出问题标题的正确句子,所以如果标题令人困惑,首先我想道歉。
所以我刚刚完成了 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
对于 InputText
不更新。
我知道您在想什么,“但是在我单击提交之前输入确实会更新”。是的,没有。
这给我们带来了与 JS 引用有关的第二个问题。
来自 React 文档
state
is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input fromstate
andprops
.
如果我们看一下
setStateFormData
你在技术上直接变异state
通过首先引用 state
中的对象与 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
然而,状态似乎正在更新,state.formdata
在 FormLogin
只是对 state.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
调用handleSubmit
.此外,您需要设置 TextInput
value
根据FormLogin
的 Prop .我认为这是最干净的解决方案,同时仍然使用高阶组件。最简单的解决方案当然是摆脱高阶组件,但如果您的目标是专门了解高阶组件,我不确定这是否是一种选择。
最后,您可以实现 getDerivedStateFromProps
FormLogin
上的功能这将重建 FormLogin
每次它的 props 发生变化时都会发生这种情况,这恰好在每次高阶状态发生变化时发生。这是更新 formdata
的干净方法在 FormLogin
它在高阶组件中发生变化的所有时间。问题是您仍然需要担心更新 formdata
每次在 FormLogin
中更改时都在高阶组件中.我希望这有帮助。
关于javascript - 如果在该函数之外设置状态,则 React JS 高阶组件不执行函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55665638/