且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

价值观Formik问题

更新时间:2023-02-27 09:24:49

我对formik并不熟悉,我只是对实现进行了快速浏览.但是,这是我在这个问题上的两分钱.

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

您正在尝试在重新渲染之前获取值,并且您拥有的值始终是旧值,因为该组件尚未更新.

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

为什么在重新呈现之前进行呼叫?

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

如果 executeChange 的值不同,并且还记录了 executeChange 取决于 setFieldValue 的情况,则会触发

handleChange state.values .

setFieldValue 是一个事件回调,仅在每次渲染后更新引用.这是来自formik docs ///,我们将引用复制到每个渲染器上的当前状态/props范围内的回调,在您的情况下尚未发生- useEventCallback -

通过操作更新状态后,您的组件将被更新,但尚未调用您的函数.

setFieldValue 将尝试验证输入,并返回一个承诺和冒泡,直到最高为 executeChange handleChange ,并将其视为低优先级,因此它不是阻塞呼叫.

一种快速的解决方案可能是使用 onKeyUp 而不是 onChange ,我认为这将绕过 useCallback 并实际上更新具有较高优先级调用的组件

  function App({值,setFieldValue}){console.log("Rerendering",values.fields [0])//-  -  -  这里  -  -  -  -  -const getValues =()=>console.log(values.fields [0]);//----------------------返回 (< div className ="formik-wrapper"><表单>< FieldArrayname ="fields"render = {()=>(<字段type ="text"name ="fields.0"placeholder =写点东西"onKeyUp = {e =>{setFieldValue("fields.0",e.target.value);getValues();}}/>)}/></表单></div>);} 

我希望这可以帮助或至少可以帮助您.

检查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"));

I am not familiar with formik and I just took a quick look on the implementation. But here's my two cents on the issue.

Why are the values not updating after each call?

You are trying to get the values before the rerendering is happening and the value you have is the old one always, because the component is not updated yet.

To verify try to add console.log(values.fields[0]); in the App component before the getValue definition.

Why is the call happening before the rerendering?

The onChange implementation will trigger the handleChange in which it's a memoized function call that will not be triggered if you have the same value BUT you changed the value.

handleChange will be triggered if the executeChange value is different and executeChange also memoized that depends on setFieldValue or the state.values.

setFieldValue is an event callback that only update the ref after each render. That's from formik docs // we copy a ref to the callback scoped to the current state/props on each render and that didn't happen yet in your case -useEventCallback-.

Once the state is updated by the action then your component will be updated but your function is not called yet.

setFieldValue will try to validate the input and return a promise and bubble that up to executeChange and handleChange and it treats that as a low priority so it's not a blocking call.

A quick solution might be is to use onKeyUp instead of onChange I think that will bypass the useCallback and actually updates the component with higher priority call

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>
  );
}

I hope this can help or at least lead for some help.

Check formik implementation