React JS Higher-Order-コンポーネントは、関数の外部で状態が設定されている場合、関数を実行しません

フルメンティウス

質問のタイトルの適切な文が思いつかないので、最初にタイトルが混乱している場合はお詫び申し上げます。

だから私はReactJSを学び、練習用のアプリを作ろうとしていました。このアプリには、入力したメールアドレスまたはパスワードが間違っている場合にアラートが表示され、フィールドが空白になるログインフォームがあります。

基本的なReactJSの実装はすべて順調ですが、コードを変更して高次のコンポーネントを実装しようとすると、アプリがバグになり、原因がどこにあるのかわからないようです。

バギー事があるので、私は電子メールまたは誤ったパスワードを入力した場合、およびフィールドが入力に私のためにブランクされるよりも、ログインをクリック/もう一度試して、思わフィールドはすべての入力を受け付け文句を言わない、それだけで、空白のまましないに関係なく、キーボードでどのキーを押したか。フィールドが入力を再び受け入れることができるようにページを更新する必要があります。

これが私のコードです:

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状態は更新さvalueInputTextません。つまり、forは更新さません。

「送信をクリックする前に入力が更新されます」とあなたが考えていることはわかっています。はいといいえ。

これにより、JS参照に関係する2番目の問題が発生します。

Reactドキュメントから

state変更が適用されているときのコンポーネントの状態への参照です。直接変更しないでください。代わりに、stateからの入力に基づいて新しいオブジェクトを作成することにより、変更を表す必要がありpropsます。

私たちが見ると、最初にでオブジェクトを参照し、次に参照setStateFormDatastateれたオブジェクトをstatelet 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古い状態からを深くコピーしています。素晴らしい、1つのバグがあります。アプリケーションを再度テストすると、送信の前後に入力が更新されないことがわかります。これは、上記の最初の問題が原因です。

以前は、状態InputText valueが更新されてFormLoginいるように見えたため、更新されているように見えましたが、state.formdatainFormLoginstate.formdataFormLoginwithのコンストラクターで設定された高階コンポーネント内の参照にすぎませんでしformdata: this.props.formdataこれは、setStateFormData高次コンポーネントの状態を直接変更すると、の状態も直接変更されることを意味しましたFormLoginこれにより、すべてが機能しているように見えましたが、実際には参照のためでした。参照が失われるとすぐに、アプリが壊れました。では、いつ参照を失ったのでしょうか。ご想像のとおり、を呼び出すsetStatehandleSubmit

self.setState({
  alertstatus: 'danger',
  alertmessage: 'Login failed. Email or password is incorrect.',
  formdata: { email: '', password: '' }
});

これは新しいオブジェクトに割り当てられstate.formdata高次コンポーネントのFormLoginへの参照を壊しstate.formdataます。

さて、それを修正する方法。いくつかの方法があります。

完全にformdata状態から外すことをお勧めFormLoginします。そうすれば、2つのformdataオブジェクトの同期を維持することを心配する必要はありませんclearFormData高階コンポーネントで関数を作成し、それをに渡してFormLoginを呼び出すだけですhandleSubmitさらに、TextInput valueの小道具に従って設定する必要がありますFormLoginこれは、高次のコンポーネントを使用しながら、最もクリーンなソリューションだと思います。

最も簡単な解決策は、もちろん高次のコンポーネントを取り除くことですが、あなたの目標が特に高次のコンポーネントについて学ぶことである場合、それがオプションであるかどうかはわかりません。

最後に、getDerivedStateFromProps関数を実装できます。この関数FormLoginではFormLogin、小道具が変更されるたび状態が再構築されます。これは、高次の状態が変更されるたびに偶然に発生します。これは、更新するためのクリーンな方法ですformdataFormLogin、それは高次の成分に変化し、すべての時間を。問題は、formdataで変更されるたびに、上位コンポーネントのを更新することを心配する必要があることですFormLogin

これがお役に立てば幸いです。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ