【问题标题】:Issue with values FormikFormik 的价值观问题
【发布时间】:2019-10-29 23:53:57
【问题描述】:

关于这个问题的 GitHub 讨论 - https://github.com/jaredpalmer/formik/issues/529

我将formik 值传递给在<Field/> 的onChange 中调用的函数,但是当我输入<Field/> 时,最后一个字符不属于该函数。

代码沙盒 - 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"));

【问题讨论】:

  • 我认为这是 setFieldValue 的问题。见github.com/jaredpalmer/formik/issues/529。它在内部使用 setState,但不返回回调,我们可以在其中调用另一个函数,就像 setState 一样。该页面中的建议不适用于您链接的 codeSandbox。如果此要求必不可少,您可能必须考虑拥有自己的状态来管理该输入。

标签: javascript html reactjs formik


【解决方案1】:

此答案基于 Formik v1.5.7

您试图在调用 setFieldValue 后立即在代码中获取陈旧的值 values,这是在内部执行异步操作,因此您不能期望在它(调用)返回后立即更改值。因此,当您尝试从values 中记录值时,您会在当前(运行)渲染周期中获得状态对象。

values 发生变化时,Formik 会重新呈现表单,在该表单中它将具有您想要获得的期望值。

我将您示例中的代码上移来解释这一点。

这是updated code link

由于这个问题是在我回答后更新的,我会评论这个具体的GitHub issue。问题在于增强开发请求和 cmets 中的解决方案在您的示例中不起作用,因为 values 仍将是“OLD”值。

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

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

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

希望澄清。

【讨论】:

    【解决方案2】:

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

    • 我不推荐 github 问题 (https://github.com/jaredpalmer/formik/issues/529) 中提到的解决方法(setTimeout、随机等待只是为了绕过 nextTick 等),因为它们是不确定的。
    • 对我有用的一种方法是使用 componentDidUpdate(或任何其他类似的 React 生命周期方法)并监听 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);
        }
      }
    }
    

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

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

      为什么每次调用后值不更新?

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

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

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

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

      handleChange 将在 executeChange 值不同时触发,并且 executeChange 也被记忆,这取决于 setFieldValuestate.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>
        );
      }
      

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

      Check formik implementation

      【讨论】:

        【解决方案4】:

        当您调用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

        【讨论】:

        • 是的,我知道,但无论如何我都需要将值数组传递给 getValues()
        猜你喜欢
        • 2020-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-21
        • 2019-12-10
        • 2020-03-08
        • 2021-11-12
        相关资源
        最近更新 更多