2017-10-23 42 views
0

私は反応するのが新しいので、私に同行してください。私はログインフォームに取り組んでいます。このフォームは、フォームが提出されてエラーが真になった後、リアルタイムで検証されます。とにかく、私はデフォルトで状態isFormValid: falseのプロパティを持っています。状態の更新が完了したかどうかを確認する方法はありますか?

これはhandleFormSubmissionのコンストラクタを介して更新します:

/** 
    * handleFormSubmission, submission method for user 
    * account login. Dispatches the login action. 
    * 
    * @param {Object} event 
    */ 
    handleFormSubmission(event) { 
    event.preventDefault(); 
    this.setState({ 
     formSubmitted: true 
    },() => { 
     this.validateForm(); 
     if (this.state.isFormValid) { 
     if (this.state.userCreatingAccount) { 
      this.props.createUserAccount(
      this.state.email, 
      this.state.password, 
      this.state.firstName, 
      this.state.lastName 
     ) 
     } else { 
      this.props.login(
      this.state.email, 
      this.state.password 
     ); 
     } 
     } 
    }); 
    } 

私は私の状態オブジェクトにformSubmitted: trueを設定し、その後、コールバックを経由して、フォームの送信を実行します。私の問題はthis.validateForm()です。 setStatethis.validateForm()の内部で一度実行された後にコードが必要です。たぶん私はそれを約束することができますか? this.validateForm().then(functio.......)。この関数は状態を一度更新するだけでなく、動的に複数回実行するので、問題が発生します。

これは私の検証のフォーム機能である:

/** 
    * function validateForm, error handling for 
    * dynamic form input fields. Resets state errors 
    * on exection, then updates state via callback. 
    */ 
    validateForm(){ 
    // Reset state. 
    this.setState({ 
     isFormValid: true, 
     errors: Object.assign(this.state.errors, { 
     email: {}, 
     password: {}, 
     firstName: {}, 
     lastName: {} 
     }) 
    },() => { 
     if (this.state.formSubmitted) { 
     // Iterate over input fields, if length is 0 
     // set an error via the state name. 
     for (var inputField in this.state.errors) { 
      if (this.state.errors.hasOwnProperty(inputField)) { 
      if (inputField !== 'email') { 
       if (
       !this.state.userCreatingAccount && 
       inputField !== 'firstName' && 
       inputField !== 'lastName' 
      ) { 
       if (this.state[inputField].length === 0) { 
        this.setState({ 
        isFormValid: false, 
        errors: Object.assign(this.state.errors, { 
         [inputField]: { 
         required: true 
         } 
        }) 
        }); 
       } 
       } else { 
       if (this.state[inputField].length === 0) { 
        this.setState({ 
        isFormValid: false, 
        errors: Object.assign(this.state.errors, { 
         [inputField]: { 
         required: true 
         } 
        }) 
        }); 
       } 
       } 
      } else { 
       // Email validation. 
       if (this.state[inputField].length === 0) { 
       this.setState({ 
        isFormValid: false, 
        errors: Object.assign(this.state.errors, { 
        [inputField]: { 
         required: true 
        } 
        }) 
       }); 
       } else if (!/^((?!.*\.\.)[a-z0-9\.\-]+[^\.]@[a-z0-9\-]+(?:\.[a-z]+)+)$/mgi.test(
       this.state[inputField] 
      )) { 
       this.setState({ 
        isFormValid: false, 
        errors: Object.assign(this.state.errors, { 
        [inputField]: { 
         invalid: true 
        } 
        }) 
       }); 
       } 
      } 
      } 
     } 
     } 
    }); 
    } 

かいつまんで、この関数は、入力フィールドの状態を更新します。これらのフィールド値に応じて1,2,3,4などのフィールドを意味する動的に行います。 this.validateForm()内のロジックを最初に終了します。その直後のコードはthis.validateForm()の内部で設定された新しい状態値に依存します。しかし、setStateは非同期です。

この質問は理にかなっていますように!前もって感謝します。

+0

したがって、関数自体が終了した後に最初のスニペットの 'this.validateForm()'の後のロジックを実行させたいのですか? – Chris

+0

'this.validateForm()'の中のロジックを最初に終了します。直後のコードは 'this.validateForm()'の内部で設定された新しい状態値に依存します。しかし、 'setState'は非同期です。 @Chris – Josh

答えて

1

Reactコンポーネントのライフサイクルを使用できると思います。私は説明についてこの記事を愛しています:http://busypeoples.github.io/post/react-component-lifecycle/、またはここからsetstateについて読むことができます:https://reactjs.org/docs/react-component.html#setstatecomponentDidUpdate()を使用して、コンポーネントの状態変更後にアクションを実行できます。コードの変更は次のとおりです。

componentDidUpdate() { 
    if (this.state.isFormValid) { 
    if (this.state.userCreatingAccount) { 
    this.props.createUserAccount(
     this.state.email, 
     this.state.password, 
     this.state.firstName, 
     this.state.lastName 
    ) 
    } else { 
    this.props.login(
     this.state.email, 
     this.state.password 
    ); 
    } 
} 

handleFormSubmission(event) { 
    event.preventDefault(); 
    this.setState({ 
     formSubmitted: true 
    },() => { 
     this.validateForm(); 
     /* move them to componentDidUpdate() */ 
     //if (this.state.isFormValid) { 
     // if (this.state.userCreatingAccount) { 
     // this.props.createUserAccount(
     //  this.state.email, 
     //  this.state.password, 
     //  this.state.firstName, 
     //  this.state.lastName 
     // ) 
     // } else { 
     // this.props.login(
     //  this.state.email, 
     //  this.state.password 
     // ); 
     // } 
     } 
    }); 
} 

希望すると、これが役に立ちます。

+0

これは、React状態が更新されるたびに 'createUserAccount()'または 'login()'関数を起動します。私はOPが望んでいるものではないと思います。 – Chris

+0

@Chrisですが、 'isFormValid'がtrueかどうかを常にチェックしています。したがって、' isFormValid'がfalseのままであれば、両方のメソッドがトリガされません –

2

コードが少し複雑すぎて実用的な例が得られませんが、私はあなたにできることのヒントを与えることができます。

私が見る最初の問題は、方法があまりにも多いことです。setState()。これはそれほど悪いことではありませんが、複雑なロジックの管理は頭痛です。また、複数の再レンダリングが偶発的に発生することもあります。

は、ここで私はどうなるのかです:

  1. は直接あなたのUIのプレゼンテーションで結ばれていない任意の状態変数をブレイク

    。たとえば、formSubmittedのようなのように見える変数は、if...elseの結果を決定するロジックでのみ使用されます。
    これを再度確認する必要があるかもしれませんが、そのような場合は、そのような変数をコンポーネントインスタンスに置きます。 this.state.formSubmittedの代わりにthis.formSubmittedのようにします。なぜこれを行うのですか?アプリケーションロジックを簡略化し、非同期化を行わないようにしてから、コールバックでロジックを続行します。

  2. validateForm()に状態を変更しないでください。その代わりに、という新しいオブジェクトを作成し、次のレンダリングで必要とする状態のものを反映させます。関数の最後に、この構築されたオブジェクトを返します。

  3. handleFormSubmission()関数では、this.setState(obj)の状態を設定できます。ここで、objは、上記の手順2から返されたオブジェクトです。 setState()のコールバックでは、ロジックの最後のビットを行います。返されたオブジェクトに必要なすべての変数が含まれている(チェックしていない)場合は、コールバックをスキップしてthis.stateではなくobjをチェックすることもできます。


TL; DRは常にアプリケーション内のいずれかのアクションやプロセスにおける単一setState()をやっ向けて努めます。これは必ずしも可能ではありませんが、99.999%の時間内でなければなりません。

幸運。

関連する問題