web-dev-qa-db-ja.com

Reduxを使用しています。 Reduxストアで制御入力状態を管理するか、コンポーネントレベルでsetStateを使用する必要がありますか?

私は反応フォームを管理するための最良の方法を見つけようとしています。 onChangeを使用してアクションを起動し、reduxストアをフォームデータで更新しようとしました。また、ローカル状態を作成しようとしました。フォームが送信されると、トリガーとアクションを実行し、reduxストアを更新します。

制御された入力状態を管理するにはどうすればよいですか?

53
gkkirsch
  1. コンポーネント自体の状態を使用できます。そして、その状態を取り、アクションへの引数として与えます。 React Docs で説明されているように、それはほとんど「React way」です。

  2. Redux Form もチェックアウトできます。それは基本的にあなたが説明したことを行い、フォーム入力をRedux Stateにリンクします。

最初の方法は基本的に、すべてを手動で行うことを意味します-最大制御と最大定型句。 2番目の方法は、高次のコンポーネントにすべての作業を任せることを意味します。そして、その間にすべてがあります。フォーム管理の特定の側面を簡素化する複数のパッケージがあります。

  1. React Forms -多数のヘルパーコンポーネントを提供して、フォームのレンダリングと検証をより簡単にします。

  2. React JSON schema -JSONスキーマからHTMLフォームを作成できます。

  3. Formsy React -説明にあるように、「React JSへのこの拡張は、柔軟性と再利用性の間の「スイートスポット」を目指しています。」

Update:Redux Formは次のように置き換えられているようです:

  1. React最終フォーム

そして、チェックアウトする価値のあるもう1つの重要な候補は次のとおりです。

  1. Formik
29
Dmitry Shvedov

Reduxの共著者の1人からのこの回答が好きです: https://github.com/reactjs/redux/issues/1287

アプリにとってグローバルに重要ではなく、複雑な方法で変化しない一時的な状態には、Reactを使用します。たとえば、一部のUI要素のトグル、フォーム入力状態。グローバルに重要であるか、複雑な方法で変異している状態(キャッシュされたユーザー、下書きなど)。

時々、Redux状態からReact状態(Reduxに何かを保存するときは厄介になる))またはその逆(より多くのコンポーネントが以前の状態にアクセスする必要があるとき)に移動したいことがあります。ローカルである)。

経験則では、次のようになります。

つまり、フォームがグローバル状態に影響しないこと、またはコンポーネントがアンマウントされた後も保持する必要があることが確実な場合は、react状態を維持します。

39
pgsandstrom

TL; DR

アプリに適していると思われるものなら何でも使用できます(出典: Redux docs


どの種類のデータをReduxに格納するかを決定するための一般的な経験則:

  • アプリケーションの他の部分はこのデータを気にしますか?
  • この元のデータに基づいて、さらに派生したデータを作成できるようにする必要がありますか?
  • 複数のコンポーネントを駆動するために同じデータが使用されていますか?
  • この状態を特定の時点に復元できるという価値はありますか(つまり、タイムトラベルのデバッグ)。
  • データをキャッシュしますか(つまり、再要求するのではなく、既に存在する場合は状態にあるものを使用します)。

これらの質問は、アプリにより適したアプローチを簡単に特定するのに役立ちます。アプリで使用するビューとアプローチは次のとおりです(フォーム用):

ローカル状態

  • フォームがUIの他のコンポーネントと関係がない場合に便利です。 input(s)からデータをキャプチャして送信するだけです。ほとんどの場合、これを単純なフォームに使用します。
  • フォームの入力フローをデバッグするタイムトラベルのユースケースはあまり見られません(他のUIコンポーネントがこれに依存していない限り)。

Redux状態

  • フォームでアプリの他のUIコンポーネントを更新する必要がある場合に便利です( two-way binding など)。
  • 私のフォームinput(s)が、ユーザーの入力内容に応じて、他のコンポーネントにrenderを引き起こす場合に使用します。
7

個人的には、すべてをRedux状態に保ち、ローカルコンポーネントの状態から離れることを強くお勧めします。これは基本的に、UIを状態の関数として見始めると、完全なブラウザーレステストを実行でき、完全な状態履歴の参照を利用できるためです(入力に含まれるもの、開かれたダイアログなど)。 、バグが発生したとき-デバッグ目的でのユーザーの状態からではありません)。 clojureの領域からの関連ツイート

編集して追加:ここで、私たちと私たちの姉妹会社が、本番アプリケーションとredux/state/uiの処理の観点から動いています。

4

ヘルパーライブラリを使用する方が簡単で、すべての決まり文句を回避できます。それらは最適化され、機能が豊富です...など。彼らはすべての異なる側面をより簡単にします。それらをテストし、さまざまなニーズに役立つものとより良いものを知るように武器を作るのは、やるべきことです。

しかし既にすべてを自分で実装している場合制御された方法に進みます。そして、理由のためにreduxが必要。私のプロジェクトの1つ。フォームの状態を維持する必要がありました。そのため、別のページに移動して戻ってきても、同じ状態のままになります。変更を複数のコンポーネントに伝える手段である場合にのみ、reduxが必要です。または、状態を保存することを意味する場合は、復元する必要があります。

状態をグローバルにする必要がある場合は、reduxが必要です。それ以外の場合は必要ありません。その点については、この素晴らしい記事 here をチェックして、深く掘り下げてください。

問題の1つ遭遇する可能性があります!制御入力を使用する場合。 キーストロークごとに変更をディスパッチするだけです。そして、フォームはfreezingで始まります。カタツムリになりました。

変更のたびにreduxフラックスを直接ディスパッチして使用しないでください。できることは、入力状態をコンポーネントのローカル状態に保存することです。そして、setState()を使用して更新します。状態が変化したら、遅延のあるタイマーを設定します。キーストロークごとにキャンセルされます。最後のキーストロークの後に、指定された遅延の後、ディスパッチアクションが続きます。(適切な遅延は500ミリ秒になる場合があります)。

setStateは、デフォルトで複数の連続したキーストロークを効果的に処理することを知っています。そうでなければ、上記と同じ手法を使用します(Vanilla jsで行うように)[しかし、ここではsetState

以下に例を示します。

_onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        // here how you implement the delay timer
        clearTimeout(this.onInputsChangeTimeoutHandler); // we clear at ever keystroke
              // this handler is declared in the constructor
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
           // this will be executed only after the last keystroke (following the delay)
            if (typeof onInputsChange === "function")
                    onInputsChange(this.state.inputs, roomId, evt);
        }, 500);
    })
}
_

次のように、小道具を使用してコンポーネントを初期化するためにアンチパターンを使用できます。

_constructor(props) {
    super(props);

    const {
        name,
        description
    } = this.props;

    this.state = {
        inputs: {
            name,
            description
        }
    }
_

コンストラクターまたは次のようなcomponentDidMountフック内:

_componentDidMount () {
    const {
        name, 
        description
    } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            name,
            description
        }
    });
}
_

後者では、コンポーネントをマウントするたびにストアから状態を復元できます。

また、親コンポーネントからフォームを変更する必要がある場合は、その親に関数を公開できます。 bindedであるsetInputs()メソッドを設定することにより。そして、構築では、props(つまり、getterメソッド)getSetInputs()を実行します。 (便利なケースは、いくつかの条件または状態でフォームをリセットする場合です)。

_constructor(props) {
    super(props);
    const {
         getSetInputs
    } = this.props;

   // .....
   if (typeof getSetInputs === 'function') getSetInputs(this.setInputs);
}
_

上記のことをよりよく理解するために、ここで入力をどのように更新していますか:

_// inputs change handlers
onNameChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            name: value
        },
        evt
    );
}

onDescriptionChange(evt) {
    const { value } = evt.target;

    this.onInputsChange(
        {
            description: value
        },
        evt
    );
}

/**
 * change = {
 *      name: value
 * }
 */
onInputsChange(change, evt) {
    const { onInputsChange, roomId } = this.props;

    this.setState({
        ...this.state,
        inputs: {
            ...this.state.inputs,
            ...change
        }
    }, () => {
        clearTimeout(this.onInputsChangeTimeoutHandler);
        this.onInputsChangeTimeoutHandler = setTimeout(() => {
            if (typeof onInputsChange === "function")
                onInputsChange(change, roomId, evt);
        }, 500);
    })
}
_

ここに私のフォームがあります:

_ const {
        name='',
        description=''
 } = this.state.inputs;

// ....

<Form className="form">
    <Row form>
        <Col md={6}>
            <FormGroup>
                <Label>{t("Name")}</Label>
                <Input
                    type="text"
                    value={name}
                    disabled={state === "view"}
                    onChange={this.onNameChange}
                />
                {state !== "view" && (
                    <Fragment>
                        <FormFeedback
                            invalid={
                                errors.name
                                    ? "true"
                                    : "false"
                            }
                        >
                            {errors.name !== true
                                ? errors.name
                                : t(
                                        "You need to enter a no existing name"
                                    )}
                        </FormFeedback>
                        <FormText>
                            {t(
                                "Enter a unique name"
                            )}
                        </FormText>
                    </Fragment>
                )}
            </FormGroup>
        </Col>
        {/* <Col md={6}>
            <div className="image">Image go here (one day)</div>
        </Col> */}
    </Row>

    <FormGroup>
        <Label>{t("Description")}</Label>
        <Input
            type="textarea"
            value={description}
            disabled={state === "view"}
            onChange={this.onDescriptionChange}
        />
        {state !== "view" && (
            <FormFeedback
                invalid={
                    errors.description
                        ? "true"
                        : "false"
                }
            >
                {errors.description}
            </FormFeedback>
        )}
    </FormGroup>
</Form>
_
0
Mohamed Allal