尝试使用 Formik 重构 onSubmit 属性

Posted

技术标签:

【中文标题】尝试使用 Formik 重构 onSubmit 属性【英文标题】:Trying to refactor the onSubmit property using Formik 【发布时间】:2021-01-19 05:30:44 【问题描述】:

使用 React 提升我的开发技能。我试图想办法重构 onSubmit 属性。我的应用程序是一个使用 Formik 组件的联系表单,它将数据发送到 Firebase Cloudstore 并通过 emailjs 发送电子邮件。如果成功,它将使用 Material UI 的 Snackbar 弹出一个弹出窗口。它有效,但只是试图清理代码。请帮忙!

onSubmit=(values,  resetForm, setSubmitting ) =>           
          emailjs.send("blah","blah", 
            email: values.email,
            name: values.name,
            message: values.message
            , 
            'blah',);    

          //this is sent to firebase cloudstore
          db.collection("contactForm")
            .add(
              name: values.name,
              email: values.email,
              message: values.message,
            )
            .then(() => 
              handleClick();
            )
            .catch((error) => 
              alert(error.message);
            );
          setTimeout(() => 
            resetForm();
            setSubmitting(false);
            /*    console.log(values);
            console.log(JSON.stringify(values, null, 2)); */
          , 500);
        

这是完整的功能

function Contact() 
  const [open, setOpen] = React.useState(false);
  const handleClose = (event, reason) => 
    if (reason === "clickaway") 
      return;
    
    setOpen(false);
  ;
  const handleClick = () => 
    setOpen(true);
  ;
  const classes = useStyles();
  return (
      <Formik
        initialValues=initialValues
        validationSchema=validationSchema
        onSubmit=(values,  resetForm, setSubmitting ) =>           
          emailjs.send("blah","blah", 
            email: values.email,
            name: values.name,
            message: values.message
            , 
            'blah',);    

          //this is sent to firebase cloudstore
          db.collection("contactForm")
            .add(
              name: values.name,
              email: values.email,
              message: values.message,
            )
            .then(() => 
              handleClick();
            )
            .catch((error) => 
              alert(error.message);
            );
          setTimeout(() => 
            resetForm();
            setSubmitting(false);
            /*    console.log(values);
            console.log(JSON.stringify(values, null, 2)); */
          , 500);
        
      >
        ( submitForm, isSubmitting ) => (
          <Form>
            <Snackbar open=open autoHideDuration=6000 onClose=handleClose>
              <Alert onClose=handleClose severity="success">
                Your message has been sent!
              </Alert>
            </Snackbar>

            <div>
              <Field
                component=TextField
                label="Name"
                name="name"
                type="name"
              />
              <ErrorMessage name="name" />
            </div>

            <div>
              <Field
                component=TextField
                label="Your email"
                name="email"
                type="email"
              />
              <ErrorMessage name="email" />
            </div>
            <br />
            <br />
            <div>
              <Field
                as="textarea"
                placeholder="Your Message"
                label="message"
                name="message"
                type="message"
                rows="15"
                cols="70"                
              />
              <ErrorMessage name="message" />
            </div>

            isSubmitting && <LinearProgress />

            <Button
              variant="contained"
              color="primary"
              disabled=isSubmitting
              onClick=submitForm
            >
              Submit
            </Button>
          </Form>
        )
      </Formik>
  );

【问题讨论】:

【参考方案1】:

我建议在组件主体中将 onSubmit 属性设为它自己的函数,您需要使用 useCallback 来记忆它。此外,您可以创建一个挂钩来控制警报组件,您还可以允许挂钩来控制天气是错误还是成功类型,从而减少在保存失败时重复代码的需要。

您的提交处理程序可能如下所示,请注意我省略了电子邮件的发送并模拟了 firebase 部分。您也可以在 promise 上调用 finally,而不是在 thencatch 块中调用 setSubmitting。

  const handleSubmit = React.useCallback(
    (values,  setSubmitting, resetForm ) => 
      db.collection("contact")
        .add(values)
        .then((res) => 
          show( message: "Your message has been sent" );
        )
        .catch((err) => 
          show( variant: "error", message: "Failed to send your message." );
        )
        .finally(() => 
          setSubmitting(false);
        );
    ,
    [show]
  );

上面示例中的 show 函数将成为控制警报的钩子的一部分。钩子可能看起来像这样,它可以根据您的用例进行扩展。

import React from "react";

const useAlert = () => 
  const [state, setState] = React.useState(
    variant: "success",
    visibile: false,
    message: null
  );

  const show = React.useCallback(
    (options = ) => 
      setState((prev) => (
        ...prev,
        ...options,
        visible: true
      ));
    ,
    [setState]
  );

  const hide = React.useCallback(() => 
    setState((prev) => ( ...prev, visibile: false ));
  , [setState]);

  return  ...state, show, hide ;
;

export default useAlert;

此外,由于您使用的是材质 ui,因此您需要利用它们的内置组件。这将消除您对多个 &lt;br /&gt;s 间距的需要,并有助于保持 UI 一致。

<Box marginBottom=1>
  <Field component=TextField label="Name" name="name" type="name" />
  <ErrorMessage name="name" />
</Box>

<Box marginBottom=1>
  <Field
    component=TextField
    label="Email"
    name="email"
    type="email"
  />
  <ErrorMessage name="email" />
</Box>

此外,您可以将内置组件用于文本区域,以保持设计一致。使用multiline prop 允许您将输入设为文本区域。

<Box marginBottom=2>
  <Field
    component=TextField
    placeholder="Your Message"
    label="Message"
    name="message"
    type="message"
    rows=5
    multiline
    fullWidth
  />
  <ErrorMessage name="message" />
</Box>

我个人不太喜欢以您的方式使用 LinearProgress。我个人认为循环过程看起来更好,特别是在提交按钮内使用时。 Here are the relevant docs.

<Button
  variant="contained"
  color="primary"
  disabled=isSubmitting
  onClick=submitForm
  endIcon=isSubmitting && <CircularProgress size=15 />
>
  Submit
</Button>

I've put a working example together in a codesandbox.

【讨论】:

哇!感谢您的努力,还有更多。我可以使用您提供的内容来清理我的代码中的许多其他内容....谢谢! 对于其他人,useCallback 有开销,仅在必要时使用。 @BluePie 你有那个来源吗?我认为与使用 useCallback 相关的成本低于在每次渲染上分配新函数的成本。

以上是关于尝试使用 Formik 重构 onSubmit 属性的主要内容,如果未能解决你的问题,请参考以下文章

让 React 表单使用 Formik 发送电子邮件

带有 Formik 的 Material UI 单选按钮 - 未设置 onSubmit 的值

将 Formik 值提升到更高级别的状态

从 Formik 组件中的上下文调用函数将导致警告

如何在不提交的情况下访问 formik 字段的当前值?

如何在 react-apollo-hooks 和 formik 中使用突变?