web-dev-qa-db-ja.com

Formikの値に関する問題

この問題に関するGitHubディスカッション- https://github.com/jaredpalmer/formik/issues/529

<Field/>のonChangeで呼び出される関数にformik値を渡しましたが、<Field/>を入力すると、最後の文字が関数に入りません。

CodeSandbox- リンク

スクリーンショット

enter image description here

最小限のコードインスタンス

import React from "react";
import ReactDOM from "react-dom";
import { Formik, Form, Field, FieldArray } from "formik";

function App() {
  //------ HERE ----------
  const getValues = values => console.log(values.fields[0]);
  //----------------------
  return (
    <>
      <Formik
        initialValues={{ fields: [""] }}
        onSubmit={(values, actions) => {
          actions.setSubmitting(false);
          console.log(values);
          return false;
        }}
        render={({ setFieldValue, values }) => (
          <Form>
            <FieldArray
              name="fields"
              render={() => (
                <Field
                  type="text"
                  name="fields.0"
                  placeholder="Write something"
                  onChange={e => {
                    setFieldValue("fields.0", e.target.value);
                    //---------------
                    getValues(values);
                    //---------------
                  }}
                />
              )}
            />
          </Form>
        )}
      />
    </>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
10
Arthur

私はformikに詳しくないので、実装について簡単に調べました。しかし、これは問題に関する私の2セントです。

呼び出しのたびに値が更新されないのはなぜですか?

コンポーネントがまだ更新されていないため、再レンダリングが発生する前に値を取得しようとしていますが、使用している値は常に古い値です。

検証するには、getValue定義の前にAppコンポーネントでconsole.log(values.fields[0]);を追加してみてください。

なぜ再レンダリングの前に通話が行われるのですか?

onChange実装はhandleChangeをトリガーします。これは、同じ値を持っている場合にトリガーされないメモ化された関数呼び出しですが、値を変更しました。

handleChange値が異なる場合、executeChangeがトリガーされ、executeChangeまたはstate.valuesに依存するsetFieldValueもメモされます。

setFieldValueは、各レンダリング後に参照を更新するだけのイベントコールバックです。これはformik docs // we copy a ref to the callback scoped to the current state/props on each renderからのものであり、あなたのケースではまだ発生していません-useEventCallback-。

アクションによって状態が更新されると、コンポーネントは更新されますが、関数はまだ呼び出されていません。

setFieldValueは入力を検証してプロミスを返し、executeChangehandleChangeまでそれをバブルし、それを低優先度として扱い、ブロッキング呼び出しではありません。

簡単な解決策はonKeyUpの代わりにonChangeを使用することかもしれません。これはuseCallbackをバイパスし、実際にはより高いコンポーネントで更新します優先コール

function App({ values, setFieldValue }) {
  console.log("Rerendering",values.fields[0])
  //------ HERE ----------
  const getValues = () => console.log(values.fields[0]);
  //----------------------

  return (
    <div className="formik-wrapper">
      <Form>
        <FieldArray
          name="fields"
          render={() => (
            <Field
              type="text"
              name="fields.0"
              placeholder="Write something"
              onKeyUp={e => {
                setFieldValue("fields.0", e.target.value);
                getValues();
              }}
            />
          )}
        />
      </Form>
    </div>
  );
}

これが役立つか、少なくともいくつかの助けになることを願っています。

formikの実装を確認

3
Seder

この回答はFormik v1.5.7に基づいています

内部で非同期操作を実行しているvaluesを呼び出した直後にコードで古い値setFieldValueを取得しようとしているため、(呼び出し)が戻った直後に値が変更されることは期待できません。したがって、valuesから値をログアウトしようとすると、現在の(実行中の)レンダリングサイクル中に状態オブジェクトが取得されます。

valuesが変更されると、Formikは、取得しようとしている目的の値を持つフォームを再レンダリングします。

これを説明するために、サンプルのコードを上に移動しました。

これが 更新されたコードリンク です。

この質問は私の回答後に更新されるため、この特定の GitHubの問題 についてコメントします。問題は拡張開発リクエストにあり、コメント内のソリューションはvaluesがまだ「古い」値であるため、この例では機能しません。

この場合、asyncawaitソリューションが機能しない理由は次のとおりです。

onChange={async e => {
    await setFieldValue("fields.0", e.target.value);
    getValues(values);
}}

上記のコードは、クリックイベントawaitsの関数呼び出しsetFieldValueで実行され、内部で実行スタックに配置された非同期操作である状態を設定します。リターンを呼び出してconsole.logが記録しますvaluesこれはたまたま既存のレンダリングサイクルvaluesです。

それが明確になることを願っています。

6
Rikin

私があなたの質問を正しく理解している場合、フィールドの値を設定し、この「新しい」値のセットに基づいて別のメソッド/関数/イベントをトリガーした直後に、Formik値の最新の状態を取得する信頼できる方法が必要です。ここでの明らかな障害は、setFieldValue()setState()を内部で使用する非同期メソッドであることです。したがって、これを達成するための直接的な方法はありません。

  • Githubの問題( https://github.com/jaredpalmer/formik/issues/529 )で言及されている回避策(setTimeout、ランダムにnextTickをバイパスするために待機するなど)はお勧めしません。それらは非決定的です。
  • 私のために働いた1つの方法は、componentDidUpdate(または他の同等のReactライフサイクルメソッド)を使用して、小道具の変更を聞くことです。
  • Formikはthis.props.valuesでReactコンポーネントの小道具のフィールド値を公開します
  • componentDidUpdateを使用する場合は、以前の小道具を新しい小道具と比較して、何かが変更されたかどうかを確認する必要があります。

これが私が何を意味するかを示すための最小限のコードです。

const _ = require('lodash');

class Sample extends React.Component {
  componentDidUpdate(prevProps) {
    if(!_.isEqual(prevProps.values, this.props.values)) {
      // At least one of the Formik fields have changed. And this.props.values holds this newest data about of the fields.
      triggerWhateverWithNewValues(this.props.values);
    }
  }
}

お役に立てれば。

2
Royzer William

getValuesを呼び出すと、古い値がsetFieldValueとして取得されます。関数呼び出しは完了していません(したがって、値はField.0に設定されていません)。ただし、値を関数getValuesに直接渡すことができます。

const getValues = valueField => console.log(valueField);


onChange={e => {
    setFieldValue("fields.0", e.target.value);
    getValues(e.target.value);
}}

https://codesandbox.io/s/spring-water-ixuuk

0
Ravi