如何在useEffect挂钩中使用Formik setFieldValue

时间:2020-02-23 03:04:36

标签: reactjs react-apollo formik use-effect

我有一个Formik表格,该表格需要根据通过路由器传递的信息进行动态更改。我需要运行graphQL查询来检索一些数据,并使用检索到的数据填充表单。我能够设置表单并检索数据,但是在使用useEffect挂钩时,我不知道如何为基础表单设置setFieldValue。我觉得我缺少一些重要的东西来访问Formik上下文,但是我无法从文档中找到它。

任何帮助都会很棒。

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

3 个答案:

答案 0 :(得分:13)

我想我已经知道了,但是不确定。我找到了一些引用Formik innerRef属性的地方,因此尝试了一下,而且似乎可行。我在文档或教程中都看不到任何地方,因此我不确定这是否是不受支持的某个功能,或者只是应该用于内部Formik东西,但它似乎对我有用在找到更好的方法之前,我将使用它。我已经花了更长的时间在此分享。 :|

欢迎提出意见或建议。或者,如果您认为这是正确的方法,则可以投票。

为解决此问题,我在函数主体中添加了useRef:

  const formikRef = useRef();

然后我将其添加为道具:

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

一旦我能够从useEffect中引用Formik函数,就我而言,我将执行以下操作:

      if (formikRef.current) {
        formikRef.current.setFieldValue(
          "platformName",
          dataGet.Platform.platformName
        );
        formikRef.current.setFieldValue(
          "platformCategory",
          dataGet.Platform.platformCategory
        );
      }

答案 1 :(得分:1)

访问Formik状态和助手的正确方法是使用Formik的useFormikContext钩子。这为您提供了所需的一切。

查看文档以获取详细信息和示例: https://formik.org/docs/api/useFormikContext

答案 2 :(得分:0)

几天前我遇到了类似的问题,我想重用一个表单来创建和更新事件。更新时,我从数据库中获取现有事件的数据,并用相应的值填充每个字段。

通过将箭头函数更改为这样的命名函数,我能够在 formik 中使用 useEffect 和 setFieldValue

    <Formik
  initialValues={initialValues}
  validationSchema={validationSchema}
  onSubmit={handleSubmit}
>
  {function ShowForm({
    values,
    errors,
    touched,
    handleChange,
    handleSubmit,
    handleBlur,
    setFieldValue,
    isValid,
    dirty,
  }) {
    useEffect(() => {
      if (!isCreateMode) {
        axios
          .get(`/event/${id}`)
          .then((response) => {
            const data = response.data.data;
            const fields = [
              "title",
              "description",
              "startDate",
              "endDate",
              "time",
              "venue",
              "link",
              "banner",
            ];
            fields.forEach((field) => {
              setFieldValue(field, data[field], false);
            });
          })
          .catch((error) => console.log("error:", error));
      }
    }, [setFieldValue]);
    return (
      <form action="" onSubmit={handleSubmit} className="event-form">
        <Grid container justify="center">
          <Grid item lg={12} style={{ textAlign: "center" }}>
            <h2>{isCreateMode ? "Create New Event" : "Edit Event"}</h2>
          </Grid>
          <Grid item lg={6}>
            <TextField
              name="title"
              id="title"
              label="Event title"
              value={values.title}
              type="text"
              onBlur={handleBlur}
              error={touched.title && Boolean(errors.title)}
              helperText={touched.title ? errors.title : null}
              variant="outlined"
              placeholder="Enter event title"
              onChange={handleChange}
              fullWidth
              margin="normal"
            />
            <Button
              variant="contained"
              disableElevation
              fullWidth
              type="submit"
              disabled={!(isValid && dirty)}
              className="event-submit-btn"
            >
              Publish Event
            </Button>
          </Grid>
        </Grid>
      </form>
    );
  }}
</Formik>;

回到您的用例,您可以简单地将表单重构为这个

return (
  <Container>
    <Formik
      initialValues={initialValues}
      validationSchema={validSchema}
      onSubmit={onSubmit}
    >
      {function myForm({
        handleSubmit,
        handleChange,
        handleBlur,
        handleReset,
        values,
        touched,
        isInvalid,
        isSubmitting,
        isValidating,
        submitCount,
        errors,
      }) {
        useEffect(() => {
          !called && getPlatformQuery({ variables: { id: formUpdateId } });
          if (dataGet && dataGet.Platform.platformName) {
            console.log(
              dataGet.Platform.platformName,
              dataGet.Platform.platformCategory
            );

            {/* run setFieldValue here */}
          }
        }, []);
        return (
          <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>
);