【问题标题】:How to Formik setFieldValue in useEffect hook如何在 useEffect 挂钩中形成 setFieldValue
【发布时间】:2020-06-07 01:47:51
【问题描述】:

我有一个 Formik 表单,它需要根据通过路由器传递的信息动态更改。我需要运行 graphQL 查询来检索一些数据并使用检索到的数据填充表单。我能够设置表单并检索数据,但我不知道如何在 useEffect 挂钩中为基础表单设置字段值。我觉得我错过了一些重要的部分来访问 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>
  );
}

【问题讨论】:

    标签: reactjs react-apollo formik use-effect


    【解决方案1】:

    我想我想通了,但不确定。我发现一些地方提到了一个 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
            );
          }
    

    【讨论】:

    • 我已经尝试过你的方法,并注意到当 formik 道具发生变化时,formikRef 并不总是得到更新。虽然我还没有为我的具体案例找到正确的解决方案,但 Daniel Schmidt 作为一篇关于此的文章 - Ref objects inside useEffect Hooks
    • 我认为所有这些助手都应该通过 useFormik() 钩子返回
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      通过将箭头函数更改为这样的命名函数,我能够在 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>
      );
      

      【讨论】:

        【解决方案4】:

        解决这个问题的一个技巧是在 Formik 表单中设置一个不可见的按钮。此按钮的onClick 将可以访问与Formik 相关的所有内容,例如setFieldValuesetTouched 等。然后您可以使用document.getElementById('..').click()useEffect“模拟”单击此按钮。这将允许您从 useEffect 执行 Formik 操作。

        例如

        // Style it to be invisible
        <Button id="testButton" type="button" onClick={() => {
            setFieldValue('test', '123');
            setTouched({});    
            // etc. any Formik action
        }}>
        </Button> 
        

        使用效果:

        useEffect(() => {
            document.getElementById("testButton").click(); // Simulate click
        }, [someVar);
        

        【讨论】:

          【解决方案5】:

          刚刚处理这个问题一段时间,发现以下解决方案利用嵌套组件内的 useFormikContext。

          // this is a component nested within a Formik Form, so it has FormikContext higher up in the dependency tree
          
          const [field, _meta, helpers] = useField(props);
          const { setFieldValue } = useFormikContext();
          const [dynamicValue, setDynamicValue] = useState('testvalue')
          
          const values = ['value1', 'value2', dynamicValue];
          
          const [selectedIndex, setSelectedIndex] = useState(field.value);
          
          // have some update operation on setting dynamicValue
          
          //handle selection 
          const handleSelect = (index) => { 
          setSelectedIndex(index);
           helpers.setField(values[index])
          }
          
          
          
          //handle the update
            useEffect(() => {
              setCustomTheme(dynamicValue);
              setFieldValue("<insertField>", dynamicValue);
            }, [dynamicValue, setFieldValue]);

          这需要一个父 Formik 元素,以便它可以正确利用 useField 和 useFormikContext。它似乎可以正确更新并且正在为我们运行。

          【讨论】:

            猜你喜欢
            • 2021-02-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-11
            • 2021-11-22
            • 2019-07-04
            • 1970-01-01
            • 2021-12-01
            相关资源
            最近更新 更多