首页 > 解决方案 > 带有打字稿的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 得到的响应

标签: reactjstypescriptformikreact-typescript

解决方案


为什么你的 HOC 不起作用

您当前的设置可能无法正常工作,因为层次结构不正确。HOC 从组件的中withFormik创建初始值props。但是 API 响应永远不会propsLogin. 它被保存到组件内部的一个状态中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 值

您可以使用setFieldValueprop 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 */
  )
};

代码沙盒链接


推荐阅读