Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Formik setFieldValue in useEffect hook

I have a Formik form that needs to dynamically change based on information passed via the router. I need to run a graphQL query to retrieve some data and populate the form with that retrieved data. I am able to set up the form and retrieve the data but I can't figure out how to setFieldValue for the underlying form while in a useEffect hook. I feel like I'm missing some important piece to get access to the Formik context but I haven't been able to figure it from the docs.

Any help would be great.

import React, { useState, useEffect } from "react";
import Router, { useRouter } from "next/router";

import Container from "react-bootstrap/Container";
import { Field, Form, FormikProps, Formik } from "formik";
import * as Yup from "yup";

import { useLazyQuery } from "@apollo/react-hooks";
import { GET_PLATFORM } from "../graphql/platforms";

export default function platformsForm(props) {
  const router = useRouter();

  // grab the action requested by caller and the item to be updated (if applicable)
  const [formAction, setFormAction] = useState(router.query.action);
  const [formUpdateId, setFormUpdateId] = useState(router.query.id);

  const [initialValues, setInitialValues] = useState({
    platformName: "",
    platformCategory: ""
  });

  const validSchema = Yup.object({
    platformName: Yup.string().required("Name is required"),
    platformCategory: Yup.string().required("Category is required")
  });

  const [
    getPlatformQuery,
    { loading, error, data: dataGet, refetch, called }
  ] = useLazyQuery(GET_PLATFORM, {
    variables: { id: formUpdateId }
  });

  useEffect(() => {
    !called && getPlatformQuery({ variables: { id: formUpdateId } });
    if (dataGet && dataGet.Platform.platformName) {
      console.log(
        dataGet.Platform.platformName,
        dataGet.Platform.platformCategory
      );

      //
      // vvv How do I set Field values at this point if I don't have Formik context
      // setFieldValue();
      //
    }
  }),
    [];

  const onSubmit = async (values, { setSubmitting, resetForm }) => {
    console.log("submitted");
    resetForm();
    setSubmitting(false);
  };

  return (
    <Container>
      <Formik
        initialValues={initialValues}
        validationSchema={validSchema}
        onSubmit={onSubmit}
      >
        {({
          handleSubmit,
          handleChange,
          handleBlur,
          handleReset,
          values,
          touched,
          isInvalid,
          isSubmitting,
          isValidating,
          submitCount,
          errors
        }) => (
          <Form>
            <label htmlFor="platformName">Name</label>
            <Field name="platformName" type="text" />

            <label htmlFor="platformCategory">Category</label>
            <Field name="platformCategory" type="text" />

            <button type="submit">Submit</button>
          </Form>
        )}
      </Formik>
    </Container>
  );
}

like image 700
Ben Riga Avatar asked Feb 23 '20 03:02

Ben Riga


2 Answers

I think I figured this out but am not sure. I found a some places that referred to aa Formik innerRef prop so tried that and it seems to work. It's not mentioned anywhere that I could see in either the doc or in the tutorial so I'm not sure if this is some function that is unsupported, or maybe just supposed to be used for internal Formik stuff but it seemed to work for me so I'm going to use that until I find a better way. I've already spent way longer on this that I care to share. :|

Comments or suggestions are welcome. Or maybe upvotes if you think this is the right approach.

To resolve this I add a useRef in the function main body:

  const formikRef = useRef();

Then I added that as a prop:

      <Formik
        innerRef={formikRef}
        initialValues={initialValues}
        validationSchema={validSchema}
        onSubmit={onSubmit}
      >

Once I did that I was able to reference the Formik functions from within useEffect so in my case I did the following:

      if (formikRef.current) {
        formikRef.current.setFieldValue(
          "platformName",
          dataGet.Platform.platformName
        );
        formikRef.current.setFieldValue(
          "platformCategory",
          dataGet.Platform.platformCategory
        );
      }
like image 79
Ben Riga Avatar answered Sep 19 '22 14:09

Ben Riga


The right way to access Formik state and helpers is to use the Formik's useFormikContext hook. This gives you everything you need.

Check the docs for details and an example: https://formik.org/docs/api/useFormikContext

like image 30
Ahmad Avatar answered Sep 19 '22 14:09

Ahmad