'Formik Initial Values out of Sync with Redux

This form is used to edit a user profile. It should load with up to date user data for the initial values. To handle this, I've added a redux fetchUser action in useEffect and set the initial values of the form to the currentUser in the redux store. fetchUser hits a database endpoint and updates the currentUser in the redux store.

However, if I submit the form and reload the page, the initial data is not updated.

I'm not sure why my initial values/form is not up to date with my redux store. The form will update with the right data if I navigate away from it and navigate back to it twice.

My assumption is that the form is not resetting the initial value each time the component is rendering. It looks like that data is persisting. I have tried passing in an object to resetForm() to force the initial values to update, but this results in the same issue. If

Here is the full react component:


import { Formik, Form, Field, ErrorMessage } from "formik"
//redux action that fetches current user
import { fetchUser } from "../../actions"
import { connect } from "react-redux"
import profileSchema from "./yupSchema"
import updateProfile from "./updateAxiosCall"

const submitData = (data, resetForm, tagNames) => {
  const newProfile = { ...data}
  //end point call - working as intended
  updateProfile(newProfile)
  resetForm()
}

const ProfileForm = ({ currentUser, fetchUser }) => {


  const [formData, setFormData] = useState(null)
  useEffect(() => {
    //grab user on component render
    fetchUser()
    setFormData({
      linkedin: currentUser.linkedin,
      github: currentUser.github,
      instagram: currentUser.instagram,
      facebook: currentUser.facebook,
      twitter: currentUser.twitter,
      header: currentUser.header,
      bio: currentUser.bio,
    })
  }, [])


  if (formData === null) {
    return <div>Not ready</div>
  } else {
    return (
        <Formik
          initialValues={formData}
          validationSchema={profileSchema}
          onSubmit={(values, { resetForm }) => {
            submitData(values, resetForm, tagNames)
          }}
        >
          {({ errors, resetForm, handleChange, touched, values, onSubmit }) => (
            <Form
              style={{
                display: "flex",
                flexDirection: "column",
                width: "100%",
              }}
              autoComplete="off"
            >
              //This is up to date
              <h1>{currentUser.header}</h1>
              <TextField
                error={errors.header && touched.header}
                onChange={handleChange}
                name="header"
                variant="outlined"
                value={values.header || ""}
                id="header"
                margin="normal"
                label="header"
                helperText={
                  errors.header && touched.header ? errors.header : null
                }
              />

         {/* ...additional <TextField> components */}
              <Button
                style={{ margin: "10px 0px" }}
                size="large"
                margin="normal"
                type="submit"
                variant="contained"
                color="primary"
              >
                Save
              </Button>
            </Form>
          )}
        </Formik>

    )
  }
}

function mapStateToProps(state) {
  return {
    currentUser: state.currentUser,
  }
}

export default connect(mapStateToProps, { fetchUser })(ProfileForm)


Solution 1:[1]

put enableReinitialize={true} as a Formik property. so this:

<Formik
enableReinitialize={true}
initialValues={formData}
...

Solution 2:[2]

@Jared answer works for me by using enableReinitialize:true hook

const formik = useFormik({
    initialValues: {
        title: "",
        text: "",
        authorid: `${user.id}`,
        cat_id: '1',
    },
    validateOnBlur: true,
    onSubmit,
    enableReinitialize: true,
});

Solution 3:[3]

You might need to separate your useEffect hook into 2 useEffects. One hook for the "componentDidMount" cycle (e.g. []) and another watching changes to currentUser.

Example:

const [formData, setFormData] = useState(null)

useEffect(() => {
  //grab user on component render
  fetchUser()
}, [])


useEffect(() => {
  setFormData({
    linkedin: currentUser.linkedin,
    github: currentUser.github,
    instagram: currentUser.instagram,
    facebook: currentUser.facebook,
    twitter: currentUser.twitter,
    header: currentUser.header,
    bio: currentUser.bio,
  })
}, [ currentUser ])

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jar
Solution 2 Salman Aziz
Solution 3