[英]React JS Higher-Order-Component not execute function if state is being set outside that function
我想不出標題合適的句子,因此,首先,我想對標題是否造成混淆表示歉意。
因此,我剛剛學習完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事件將以某種方式拒絕工作以更新輸入更改中的字段。
有人請告訴我是什么導致了這種行為?
我只想說您不需要在這里使用高階組件,但是您嘗試執行的操作是可以的。
我看到幾個問題。
首先,您要根據FormLogin
狀態設置InputText
value
。 很好,但是,當InputText
更改時,它會調用onInputChange
,后者從更高組件中設置為setStateFormData
。 問題在於setStateFormData
設置高階組件的狀態,而不是FormLogin
的狀態。 因此,結果是FormLogin
狀態不會更新,這意味着InputText
的value
不會更新。
我知道您在想什么,“但是輸入確實會在我單擊“提交”之前更新”。 是的,沒有。
這將我們帶到與JS引用有關的第二個問題。
來自React文檔
state
是在應用更改時對組件狀態的引用。 它不應該直接突變。 相反,更改應通過根據state
和props
的輸入構建一個新對象來表示。
如果我們看一下setStateFormData
,從技術上講,您是在state
上let tempData = state.formdata;
state
是先使用let tempData = state.formdata;
引用state
的一個對象let tempData = state.formdata;
然后用tempData[field] = value;
修改引用的對象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
。 太好了,發現了一個bug。 如果再次測試應用程序,現在我們看到輸入在提交之前或之后永遠不會更新。 這是由於上述第一個問題。
之前, InputText
value
似乎被更新,因為FormLogin
狀態卻出現了被更新, state.formdata
在FormLogin
只是一個參考state.formdata
在其中在構造函數中設置的高次成分FormLogin
與formdata: this.props.formdata
這意味着,當setStateFormData
直接改變高階組件的狀態時,它也直接改變了FormLogin
的狀態。 這看起來一切正常,但這實際上只是由於引用。 一旦丟失參考,應用程序就會崩潰。 那么,我們什么時候失去參考? 如您所料,當我們在handleSubmit
調用setState
時。
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
。 另外,您需要根據FormLogin
設置TextInput
value
。 我認為這是最干凈的解決方案,同時仍使用高階組件。
最簡單的解決方案當然就是擺脫高階組件,但是如果您的目標是專門學習高階組件,我不確定這是否是一個選擇。
最后,你可以實現一個getDerivedStateFromProps上功能FormLogin
這將重建FormLogin
每一個道具的變更時間巧合發生的每一次高階狀態的變化狀態。 這是一種在高級組件中FormLogin
更改時都在formdata
中更新formdata
的干凈方法。 問題是你還需要擔心更新formdata
每次在發生變化時的高次成分FormLogin
。
我希望這有幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.