簡體   English   中英

如果在該函數外部設置狀態,則React JS Higher-Order-Component不執行該函數

[英]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狀態不會更新,這意味着InputTextvalue不會更新。

我知道您在想什么,“但是輸入確實會在我單擊“提交”之前更新”。 是的,沒有。

這將我們帶到與JS引用有關的第二個問題。

來自React文檔

state是在應用更改時對組件狀態的引用。 它不應該直接突變。 相反,更改應通過根據stateprops的輸入構建一個新對象來表示。

如果我們看一下setStateFormData ,從技術上講,您是在statelet 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.formdataFormLogin只是一個參考state.formdata在其中在構造函數中設置的高次成分FormLoginformdata: 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM