この問題に関するGitHubディスカッション- https://github.com/jaredpalmer/formik/issues/529
<Field/>
のonChangeで呼び出される関数にformik値を渡しましたが、<Field/>
を入力すると、最後の文字が関数に入りません。
CodeSandbox- リンク
スクリーンショット:
最小限のコードインスタンス:
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"));
私は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
は入力を検証してプロミスを返し、executeChange
とhandleChange
までそれをバブルし、それを低優先度として扱い、ブロッキング呼び出しではありません。
簡単な解決策は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 v1.5.7に基づいています
内部で非同期操作を実行しているvalues
を呼び出した直後にコードで古い値setFieldValue
を取得しようとしているため、(呼び出し)が戻った直後に値が変更されることは期待できません。したがって、values
から値をログアウトしようとすると、現在の(実行中の)レンダリングサイクル中に状態オブジェクトが取得されます。
values
が変更されると、Formikは、取得しようとしている目的の値を持つフォームを再レンダリングします。
これを説明するために、サンプルのコードを上に移動しました。
これが 更新されたコードリンク です。
この質問は私の回答後に更新されるため、この特定の GitHubの問題 についてコメントします。問題は拡張開発リクエストにあり、コメント内のソリューションはvalues
がまだ「古い」値であるため、この例では機能しません。
この場合、async
await
ソリューションが機能しない理由は次のとおりです。
onChange={async e => {
await setFieldValue("fields.0", e.target.value);
getValues(values);
}}
上記のコードは、クリックイベントawaits
の関数呼び出しsetFieldValue
で実行され、内部で実行スタックに配置された非同期操作である状態を設定します。リターンを呼び出してconsole.log
が記録しますvalues
これはたまたま既存のレンダリングサイクルvalues
です。
それが明確になることを願っています。
私があなたの質問を正しく理解している場合、フィールドの値を設定し、この「新しい」値のセットに基づいて別のメソッド/関数/イベントをトリガーした直後に、Formik値の最新の状態を取得する信頼できる方法が必要です。ここでの明らかな障害は、setFieldValue()
がsetState()
を内部で使用する非同期メソッドであることです。したがって、これを達成するための直接的な方法はありません。
componentDidUpdate
(または他の同等のReactライフサイクルメソッド)を使用して、小道具の変更を聞くことです。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);
}
}
}
お役に立てれば。
getValues
を呼び出すと、古い値がsetFieldValue
として取得されます。関数呼び出しは完了していません(したがって、値はField.0
に設定されていません)。ただし、値を関数getValues
に直接渡すことができます。
const getValues = valueField => console.log(valueField);
onChange={e => {
setFieldValue("fields.0", e.target.value);
getValues(e.target.value);
}}