Updating one of the Formik initial values with state resets all other values

Viewed 4748

I currently have a form I am building using formik. I am able to update each of the fields(name, email, phone) as expected. The issue I am running into is when I provide one of the formik intial values with a state value and update that state. The act of updating the state causes the form to completely reset the input fields Is there a way to update the test state without resetting the entire form?? My code is as follows:

import { useState } from "react";
import { Formik, FieldArray } from "formik";
import "./styles.css";

export default function App() {
  const [test, setTest] = useState(null);
  return (
    <Formik
      enableReinitialize
      initialValues={{
        test: test,
        name: "",
        email: "",
        phone: ""
      }}
    >
      {({
        values,
        touched,
        errors,
        handleChange,
        handleBlur,
        handleSubmit
      }) => (
        <>
          <button onClick={() => setTest("something")}>Update State</button>
          <br />
          <input
            placeholder="name"
            type="text"
            name="name"
            value={values.name}
            onChange={handleChange}
          />
          <br />
          <input
            placeholder="email"
            type="text"
            name="email"
            value={values.email}
            onChange={handleChange}
          />
          <br />
          <input
            placeholder="phone"
            type="text"
            name="phone"
            value={values.phone}
            onChange={handleChange}
          />
          <>
            <pre style={{ textAlign: "left" }}>
              <strong>Values</strong>
              <br />
              {JSON.stringify(values, null, 2)}
            </pre>
            <pre style={{ textAlign: "left" }}>
              <strong>Errors</strong>
              <br />
              {JSON.stringify(errors, null, 2)}
            </pre>
          </>
        </>
      )}
    </Formik>
  );
}


In order to replicate what I am talking about in the codesandbox first update the name, email, and phone number. Then hit the udate state button and everything will be reset

codesandbox https://codesandbox.io/s/wispy-sun-mzeuy?file=/src/App.js:0-1567

2 Answers

This is because you are initializing the Formik component with enableReinitialize set to true:

enableReinitialize?: boolean

Default is false. Control whether Formik should reset the form if initialValues changes (using deep equality).

When the state variable test is updated, the formik-values are reinitialized to the values set in initialValues.


If you want this to work, I've forked your sandbox and modified it to use the formik hook (without enableReinitialize) in combination with a useEffect to manually update the value from state:

  const [test, setTest] = useState(null);
  const { values, errors, handleChange, setFieldValue } = useFormik({
    initialValues: {
      test: test,
      name: "",
      email: "",
      phone: ""
    }
  });

  useEffect(() => {
    setFieldValue("test", test);
  }, [test]);

My solution for this problem was to use the Formik values as state and set it directly with setFieldValue, so enableInitialize would not be triggered.

export default function App() {
  //const [test, setTest] = useState(null); // This removed
  return (
    <Formik
      enableReinitialize
      initialValues={{
        test: "",
        name: "",
        email: "",
        phone: ""
      }}
    >

...

 <button onClick={() => setFieldValue("test", "something")}>Update State</button>

Mostly the reason you would want to use states outside of Formik is to use it outside of Formik (ok clear), but this way you still can trigger a function from outside of Formik which you pass the values and the setFieldValue- Function to do whatever when triggered e.g. at onChange of a Formik field:

  const handlerDefinedOutside = (values: any, setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void): void => { /* do stuff */ };

  <TextField
     onChange={(e) => {
       handleChange(e);
       handlerDefinedOutside(values, setFieldValue);
     }}
  />
Related