价值观问题 Formik
Issue with values Formik
GitHub discussion about this issue - https://github.com/jaredpalmer/formik/issues/529
我将 formik 值传递给在 <Field/>
的 onChange 中调用的函数,但是当我输入 <Field/>
时,最后一个字符不会落入函数中。
CodeSandbox - link
截图:
最小代码实例:
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 v1.5.7
您试图在调用 setFieldValue
后立即在您的代码中获取 values
的陈旧值,它在内部执行异步操作,因此您不能期望值在它之后立即更改(调用)returns。因此,当您尝试从 values
中记录值时,您会在当前 (运行) 渲染周期中获得状态对象。
当 values
发生变化时,Formik re-renders 将具有您想要获得的期望值的形式。
我在你的示例中向上移动了代码来解释这一点。
由于这个问题是在我回答之后更新的,所以我将针对这个具体问题发表评论 GitHub issue。问题出在增强开发请求中,注释中的解决方案在您的示例中不起作用,因为 values
仍然是 "OLD" 值。
async
await
解决方案在这种情况下不起作用的原因如下:
onChange={async e => {
await setFieldValue("fields.0", e.target.value);
getValues(values);
}}
以上代码在名为 setFieldValue
的函数上发生单击事件 awaits
,该函数被执行并在内部设置状态,这是放置在执行堆栈中的异步操作。调用 returns 和 console.log
记录 values
恰好是现有渲染周期 values
。
希望澄清。
当您调用 getValues
时,它会得到旧值,因为 setFieldValue
函数调用未完成(因此值未设置为 Field.0
)。不过,您可以直接将值传递给函数 getValues
。
const getValues = valueField => console.log(valueField);
onChange={e => {
setFieldValue("fields.0", e.target.value);
getValues(e.target.value);
}}
我对 formik 不熟悉,只是快速浏览了一下实现。
但这是我在这个问题上的两分钱。
为什么每次调用后值都没有更新?
您正在尝试在重新渲染发生之前获取值,而您拥有的值始终是旧值,因为组件尚未更新。
要验证,请尝试在 App 组件中的 getValue 定义之前添加 console.log(values.fields[0]);
。
为什么调用发生在重新渲染之前?
onChange
实现将触发 handleChange
,其中它是一个记忆函数调用,如果您具有相同的值但您更改了该值,则不会被触发。
handleChange
将在 executeChange
值不同时触发,并且 executeChange
也会根据 setFieldValue
或 state.values
记忆。
setFieldValue
是一个事件回调,它只在每次渲染后更新 ref。那是来自 formik 文档 // we copy a ref to the callback scoped to the current state/props on each render
并且在你的情况下还没有发生 -useEventCallback
-.
一旦操作更新了状态,您的组件就会更新,但您的函数尚未被调用。
setFieldValue
将尝试验证输入和 return 承诺并冒泡到 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 值的最新状态,在设置一个字段的值之后立即触发另一个 method/function/event 基于这组 'new'值。这里明显的障碍是 setFieldValue()
是一种内部使用 setState()
的异步方法,因此没有直接的方法以您描述/预期的方式实现此目的。
- 我不推荐 github 问题 (https://github.com/jaredpalmer/formik/issues/529) 中提到的解决方法(setTimeout、随机等待只是为了绕过 nextTick 等),因为它们是不确定的。
- 一种对我有用的方法是使用
componentDidUpdate
(或任何其他类似的 React Lifecycle 方法)并监听 props 的变化。
- Formik 在
this.props.values
公开 React 组件的 props 中的字段值
- 当您使用
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);
}
}
}
希望对您有所帮助。
GitHub discussion about this issue - https://github.com/jaredpalmer/formik/issues/529
我将 formik 值传递给在 <Field/>
的 onChange 中调用的函数,但是当我输入 <Field/>
时,最后一个字符不会落入函数中。
CodeSandbox - link
截图:
最小代码实例:
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 v1.5.7
您试图在调用 setFieldValue
后立即在您的代码中获取 values
的陈旧值,它在内部执行异步操作,因此您不能期望值在它之后立即更改(调用)returns。因此,当您尝试从 values
中记录值时,您会在当前 (运行) 渲染周期中获得状态对象。
当 values
发生变化时,Formik re-renders 将具有您想要获得的期望值的形式。
我在你的示例中向上移动了代码来解释这一点。
由于这个问题是在我回答之后更新的,所以我将针对这个具体问题发表评论 GitHub issue。问题出在增强开发请求中,注释中的解决方案在您的示例中不起作用,因为 values
仍然是 "OLD" 值。
async
await
解决方案在这种情况下不起作用的原因如下:
onChange={async e => {
await setFieldValue("fields.0", e.target.value);
getValues(values);
}}
以上代码在名为 setFieldValue
的函数上发生单击事件 awaits
,该函数被执行并在内部设置状态,这是放置在执行堆栈中的异步操作。调用 returns 和 console.log
记录 values
恰好是现有渲染周期 values
。
希望澄清。
当您调用 getValues
时,它会得到旧值,因为 setFieldValue
函数调用未完成(因此值未设置为 Field.0
)。不过,您可以直接将值传递给函数 getValues
。
const getValues = valueField => console.log(valueField);
onChange={e => {
setFieldValue("fields.0", e.target.value);
getValues(e.target.value);
}}
我对 formik 不熟悉,只是快速浏览了一下实现。 但这是我在这个问题上的两分钱。
为什么每次调用后值都没有更新?
您正在尝试在重新渲染发生之前获取值,而您拥有的值始终是旧值,因为组件尚未更新。
要验证,请尝试在 App 组件中的 getValue 定义之前添加 console.log(values.fields[0]);
。
为什么调用发生在重新渲染之前?
onChange
实现将触发 handleChange
,其中它是一个记忆函数调用,如果您具有相同的值但您更改了该值,则不会被触发。
handleChange
将在 executeChange
值不同时触发,并且 executeChange
也会根据 setFieldValue
或 state.values
记忆。
setFieldValue
是一个事件回调,它只在每次渲染后更新 ref。那是来自 formik 文档 // we copy a ref to the callback scoped to the current state/props on each render
并且在你的情况下还没有发生 -useEventCallback
-.
一旦操作更新了状态,您的组件就会更新,但您的函数尚未被调用。
setFieldValue
将尝试验证输入和 return 承诺并冒泡到 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 值的最新状态,在设置一个字段的值之后立即触发另一个 method/function/event 基于这组 'new'值。这里明显的障碍是 setFieldValue()
是一种内部使用 setState()
的异步方法,因此没有直接的方法以您描述/预期的方式实现此目的。
- 我不推荐 github 问题 (https://github.com/jaredpalmer/formik/issues/529) 中提到的解决方法(setTimeout、随机等待只是为了绕过 nextTick 等),因为它们是不确定的。
- 一种对我有用的方法是使用
componentDidUpdate
(或任何其他类似的 React Lifecycle 方法)并监听 props 的变化。 - Formik 在
this.props.values
公开 React 组件的 props 中的字段值
- 当您使用
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);
}
}
}
希望对您有所帮助。