价值观问题 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 将具有您想要获得的期望值的形式。

我在你的示例中向上移动了代码来解释这一点。

这是 updated code link

由于这个问题是在我回答之后更新的,所以我将针对这个具体问题发表评论 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);
}}

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

我对 formik 不熟悉,只是快速浏览了一下实现。 但这是我在这个问题上的两分钱。

为什么每次调用后值都没有更新?

您正在尝试在重新渲染发生之前获取值,而您拥有的值始终是旧值,因为组件尚未更新。

要验证,请尝试在 App 组件中的 getValue 定义之前添加 console.log(values.fields[0]);

为什么调用发生在重新渲染之前?

onChange 实现将触发 handleChange,其中它是一个记忆函数调用,如果您具有相同的值但您更改了该值,则不会被触发。

handleChange 将在 executeChange 值不同时触发,并且 executeChange 也会根据 setFieldValuestate.values 记忆。

setFieldValue 是一个事件回调,它只在每次渲染后更新 ref。那是来自 formik 文档 // we copy a ref to the callback scoped to the current state/props on each render 并且在你的情况下还没有发生 -useEventCallback-.

一旦操作更新了状态,您的组件就会更新,但您的函数尚未被调用。

setFieldValue 将尝试验证输入和 return 承诺并冒泡到 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>
  );
}

我希望这可以有所帮助,或者至少可以提供一些帮助。

Check formik implementation

如果我正确理解你的问题,你需要一种可靠的方法来获取 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);
    }
  }
}

希望对您有所帮助。