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