Issue with values Formik

Art*_*hur 9 html javascript reactjs formik

GitHub discussion about this issue - https://github.com/jaredpalmer/formik/issues/529

I passed formik values to a function that is called in <Field/>'s onChange but when I type in the <Field/>, the last character does not fall into the function.

CodeSandbox - link

Screenshot:

在此处输入图片说明

Minimal Code Instance:

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"));
Run Code Online (Sandbox Code Playgroud)

Sed*_*der 10

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

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

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

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

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

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

handleChange如果executeChange值不同,则将被触发,并executeChange根据setFieldValue或 进行记忆state.values

setFieldValue是一个事件回调,只在每次渲染后更新 ref。那来自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>
  );
}
Run Code Online (Sandbox Code Playgroud)

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

检查formik实现


Rik*_*kin 6

该答案基于Formik v1.5.7

您正在尝试values在调用后立即在代码中获取过时的值,该值在setFieldValue内部正在执行异步操作,因此您不能期望值在(调用)返回后立即被更改。因此,当您尝试从values当前(运行)渲染周期中注销值时,会得到状态对象。

valuesdo更改时,Formik重新呈现将具有您想要获得的期望值的形式。

我在您的示例中上移了代码以进行解释。

这是更新的代码链接

由于此问题是在我回答后更新的,因此我将对此特定的GitHub问题发表评论。问题出在增强开发请求中,注释中的解决方案在您的示例中不起作用,因为values仍然是“ OLD”值。

这是async await在这种情况下解决方案不起作用的原因:

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

上面的代码在执行awaits函数调用时单击事件,setFieldValue并在内部设置状态,该状态是放置在执行堆栈中的异步操作。呼叫返回和console.log日志values恰好是现有的渲染周期values

希望能澄清。


Roy*_*raj 6

如果我正确理解您的问题,您需要一种可靠的方法来获取 Formik 值的最新状态,在设置字段的值并基于此“新”值集触发另一个方法/函数/事件之后。这里明显的障碍是这setFieldValue()是一种内部使用的异步方法,setState()因此没有直接的方法以您描述/预期的方式实现这一点。

  • 我不会推荐 github 问题 ( https://github.com/jaredpalmer/formik/issues/529 ) 中提到的解决方法(setTimeout、随机等待只是为了绕过 nextTick 等),因为它们是不确定的。
  • 对我componentDidUpdate有用的一种方法是使用(或任何其他类似的 React Lifecycle 方法)并监听 props 的变化。
  • Formik 在 React 组件的 props 中公开字段值 this.props.values
  • 当您使用 时componentDidUpdate,您只需要将之前的 props 与新的 props 进行比较以检查是否发生了某些变化。

这是一个最小的代码来显示我的意思。

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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助。