如何在 useEffect 挂钩中使用 Formik setFieldValue
How to Formik setFieldValue in useEffect hook
我有一个 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>
);
}
我想我明白了,但不确定。我发现一些地方提到了一个 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 状态和助手的正确方法是使用 Formik 的 useFormikContext
钩子。这为您提供了所需的一切。
查看文档以获取详细信息和示例:
https://formik.org/docs/api/useFormikContext
我 运行 几天前遇到了类似的问题,我想重用一个表单来创建和更新事件。更新时,我从数据库中获取一个已经存在的事件的数据,并用相应的值填充每个字段。
通过将箭头函数更改为这样的命名函数,我能够在 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>
);
解决这个问题的一个技巧是在 Formik 表单中设置一个不可见的按钮。此按钮的 onClick
将可以访问与 Formik 相关的所有内容,例如 setFieldValue
、setTouched
等。然后您可以“模拟”从 useEffect
使用此按钮的点击document.getElementById('..').click()
。这将允许您从 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);
刚刚处理了这个问题一段时间,发现了以下在嵌套组件中利用 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。它似乎更新正确,对我们来说 运行。
我有一个 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>
);
}
我想我明白了,但不确定。我发现一些地方提到了一个 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 状态和助手的正确方法是使用 Formik 的 useFormikContext
钩子。这为您提供了所需的一切。
查看文档以获取详细信息和示例: https://formik.org/docs/api/useFormikContext
我 运行 几天前遇到了类似的问题,我想重用一个表单来创建和更新事件。更新时,我从数据库中获取一个已经存在的事件的数据,并用相应的值填充每个字段。
通过将箭头函数更改为这样的命名函数,我能够在 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>
);
解决这个问题的一个技巧是在 Formik 表单中设置一个不可见的按钮。此按钮的 onClick
将可以访问与 Formik 相关的所有内容,例如 setFieldValue
、setTouched
等。然后您可以“模拟”从 useEffect
使用此按钮的点击document.getElementById('..').click()
。这将允许您从 useEffect
.
例如
// 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);
刚刚处理了这个问题一段时间,发现了以下在嵌套组件中利用 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。它似乎更新正确,对我们来说 运行。