reactjs - 带有打字稿的formik在withFormik HOC中设置了初始值
问题描述
我正在使用打字稿添加 formik 并对我的项目做出反应,所以我使用 withFormik 钩子作为 HOC,我面临的问题是我无法为我从 api 获得的响应设置初始值(在 hoc 中定义)
登录.tsx
import { FormikProps } from "formik";
export interface FormValues {
title: string;
}
export const Login: React.FC<FormikProps<FormValues>> = (props) => {
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => setTitle(json.title));
}, []);
const [title, setTitle] = useState<string | null>(null);
const { handleSubmit, handleBlur, handleChange, touched, errors, values } =
props;
return (
<Container component="main" maxWidth="xs">
<div className={classes.paper}>
<form className={classes.form} noValidate onSubmit={handleSubmit}>
<TextField
variant="outlined"
margin="normal"
required
fullWidth
id="title"
label="Title"
name="title"
value={values.title}
autoFocus
autoComplete="off"
onChange={handleChange}
onBlur={handleBlur}
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
>
Sign In
</Button>
</form>
</div>
</Container>
);
};
登录.hoc.tsx
import { Form, FormikProps, FormikValues, useFormik, withFormik } from "formik";
import { FormValues, Login } from "./index";
interface MyFormikProps {
}
export const LoginView = withFormik<MyFormikProps, FormValues>({
enableReinitialize: true,
mapPropsToValues: (props) => {
return { title:"" };
},
handleSubmit: (values) => {
console.log(values);
},
})(Login);
这工作得很好,但我的问题是假设我在 login.tsx 的 didmount 中点击了一个 api,然后你可以看到我设置了“标题”来响应我从 api 得到的内容
现在我想将“title”的 initialValue 设置为我从 api 得到的响应
解决方案
为什么你的 HOC 不起作用
您当前的设置可能无法正常工作,因为层次结构不正确。HOC 从组件的中withFormik
创建初始值props
。但是 API 响应永远不会props
在Login
. 它被保存到组件内部的一个状态中Login
。外部的 HOC无法访问它。
您可以设置一个在其中withFormik
获取正确 API 数据的设置props
,但您需要将 API 获取提升到更高级别。
// component which renders the form, no more API fetch
export const Login: React.FC<FormikProps<FormValues>> = (props) => {/*...*/}
// data passed down from API
interface MyFormikProps {
title: string | null;
}
// component which sets up the form
export const LoginView = withFormik<MyFormikProps, FormValues>({
enableReinitialize: true,
mapPropsToValues: (props) => ({
title: props.title ?? "" // fallback if null
}),
handleSubmit: (values) => {
console.log(values);
}
})(Login);
// component which loads the data
export const ApiLoginForm = () => {
const [title, setTitle] = useState<string | null>(null);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => setTitle(json.title));
}, []);
return <LoginView title={title} />;
};
处理异步初始值
您的 API 中的值需要时间来解决。将其用作初始值很棘手,因为一旦安装表单,初始值就必须存在。我们在这里所做的是回退到使用空字符串''
作为初始值。然后,一旦 API 响应加载,表单将使用新的初始值重置,因为您使用了enableReinitialize
设置。
这将重置整个表单,如果 API 响应缓慢,可能会导致糟糕的用户体验。如果用户在响应返回之前在表单中输入了文本,您不希望覆盖用户输入。在响应可用之前,您可以禁用该字段。touched.title
或者您可以通过检查 if istrue
或 if来使修改成为有条件的values.title
。如果您想更深入地了解如何修改表单,请继续阅读...
以编程方式修改 Formik 值
您可以使用setFieldValue
prop fromFormikProps
来强制修改字段的任何值。您可以通过调用来更改当前值,而不是更改初始值setFieldValue
。
这种方法更符合您当前的设置,因为它允许您将 API 调用保留在Login
. 我们不需要本地title
状态。setTitle
您可以调用setFieldValue
.
export const Login: React.FC<FormikProps<FormValues>> = (props) => {
// access setFieldValue from props
const { setFieldValue, handleSubmit, handleBlur, handleChange, touched, errors, values } =
props;
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
// could make this conditional, or add extra logic here
.then((json) => setFieldValue("title", json.title));
}, []);
return (
/* same JSX as before */
)
};
推荐阅读
- python - 忽略行首的大写单词,用正则表达式替换其他单词?
- c# - 为什么此 Roslyn 分析仪测试失败?
- javascript - Javascript类原型中的函数语句和函数表达式有什么区别?
- flutter - 如何在 Flutter 中进行芯片按键设计
- css - 设置根字体大小不影响 Safari 中的边距、填充等 rem 单位
- javascript - 如何在 JavaScript 中访问函数调用上下文?
- c# - 有什么方法可以让 C# 编译器在编译时检查更多?
- javascript - 有自定义图表的方法吗?
- javascript - React Hooks - 使用 prevState 时防止重新渲染
- c# - 文本文件编写器和阅读器不适用于我的 WPF