React Formik在<Formik />之外使用SubmitForm

ssu*_*hat 8 reactjs formik

当前行为

<Formik
    isInitialValid
    initialValues={{ first_name: 'Test', email: 'test@mail.com' }}
    validate={validate}
    ref={node => (this.form = node)}
    onSubmitCallback={this.onSubmitCallback}
    render={formProps => {
        const fieldProps = { formProps, margin: 'normal', fullWidth: true, };
        const {values} = formProps;
        return (
            <Fragment>
                <form noValidate>
                    <TextField
                        {...fieldProps}
                        required
                        autoFocus
                        value={values.first_name}
                        type="text"
                        name="first_name"

                    />

                    <TextField
                        {...fieldProps}
                        name="last_name"
                        type="text"
                    />

                    <TextField
                        {...fieldProps}
                        required
                        name="email"
                        type="email"
                        value={values.email}

                    />
                </form>
                <Button onClick={this.onClick}>Login</Button>
            </Fragment>
        );
    }}
/>
Run Code Online (Sandbox Code Playgroud)

我正在尝试此解决方案https://github.com/jaredpalmer/formik/issues/73#issuecomment-317169770但它总是让我返回Uncaught TypeError: _this.props.onSubmit is not a function

当我尝试console.log(this.form)有submitForm功能。

有解决方案的人吗?


-Formik版本:最新-React版本:v16-操作系统:Mac OS

Eri*_*tin 70

仅适用于想知道通过 React hooks 解决什么问题的人:

Formik 2.x,如本答案所述

// import this in the related component
import { useFormikContext } from 'formik';

// Then inside the component body
const { submitForm } = useFormikContext();

const handleSubmit = () => {
  submitForm();
}
Run Code Online (Sandbox Code Playgroud)

请记住,该解决方案仅适用于Formik 组件内的组件,因为它使用上下文 API。如果由于某种原因您想从外部组件手动提交,或者从实际使用 Formik 的组件手动提交,您实际上仍然可以使用innerRefprop。

TLDR ; 如果您提交的组件是 a<Formik>或withFormik()组件的子组件,则此上下文答案就像一个魅力,否则,请使用innerRef下面的答案。

Formik 1.5.x+

// Attach this to your <Formik>
const formRef = useRef()

const handleSubmit = () => {
  if (formRef.current) {
    formRef.current.handleSubmit()
  }
}

// Render
<Formik innerRef={formRef} />
Run Code Online (Sandbox Code Playgroud)

  • 在 TypeScript 中使用 `useRef` 不要忘记设置 FormikValues 类型 `useRef&lt;FormikValues&gt;()` (12认同)
  • 在 TypeScript 中使用 `useRef` 我发现 2021 年的正确方法是 `useRef&lt;FormikProps&lt;FormikValues&gt;&gt;(null)` (11认同)
  • Formik 1.5.x 的最佳解决方案。对于Formik 2.x,请参阅ZEE发布的解决方案 (2认同)

Eli*_*ant 24

您可以将formikProps.submitForm(Formik的程序化提交)绑定到父组件,然后触发父组件的提交:

import React from 'react';
import { Formik } from 'formik';

class MyForm extends React.Component {
    render() {
        const { bindSubmitForm } = this.props;
        return (
            <Formik
                initialValues={{ a: '' }}
                onSubmit={(values, { setSubmitting }) => {
                    console.log({ values });
                    setSubmitting(false);
                }}
            >
                {(formikProps) => {
                    const { values, handleChange, handleBlur, handleSubmit } = formikProps;

                    // bind the submission handler remotely
                    bindSubmitForm(formikProps.submitForm);

                    return (
                        <form noValidate onSubmit={handleSubmit}>
                            <input type="text" name="a" value={values.a} onChange={handleChange} onBlur={handleBlur} />
                        </form>
                    )
                }}
            </Formik>
        )
    }
}

class MyApp extends React.Component {

    // will hold access to formikProps.submitForm, to trigger form submission outside of the form
    submitMyForm = null;

    handleSubmitMyForm = (e) => {
        if (this.submitMyForm) {
            this.submitMyForm(e);
        }
    };
    bindSubmitForm = (submitForm) => {
        this.submitMyForm = submitForm;
    };
    render() {
        return (
            <div>
                <button onClick={this.handleSubmitMyForm}>Submit from outside</button>
                <MyForm bindSubmitForm={this.bindSubmitForm} />
            </div>
        )
    }
}

export default MyApp;
Run Code Online (Sandbox Code Playgroud)

  • 如何用函数做到这一点?不太确定如何使用书籍。 (4认同)

小智 8

我找到的最佳解决方案在这里描述/sf/answers/3750163231/

在此处复制答案:

将“id”属性添加到您的表单中:id='my-form'

class CustomForm extends Component {
    render() {
        return (
             <form id='my-form' onSubmit={alert('Form submitted!')}>
                // Form Inputs go here    
             </form>
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在表单外的目标按钮的“form”属性中添加相同的Id:

<button form='my-form' type="submit">Outside Button</button>
Run Code Online (Sandbox Code Playgroud)

现在,“外部按钮”按钮将完全等效,就好像它在表单内部一样。

注意:IE11 不支持此功能。


小智 6

我刚刚遇到了同样的问题,并找到了一个非常简单的解决方案,希望这会有所帮助:

这个问题可以用 plain 解决html。如果你id在你的标签上放了一个标签,form那么你可以使用按钮的form标签用你的按钮来定位它。

例子:

      <button type="submit" form="form1">
        Save
      </button>
      <form onSubmit={handleSubmit} id="form1">
           ....
      </form>
Run Code Online (Sandbox Code Playgroud)

您可以将表单和按钮放在任何地方,甚至可以分开。

然后,此按钮将触发表单提交功能,formik 将像往常一样捕获该过程。(只要在呈现按钮时将表单呈现在屏幕上,那么无论表单和按钮位于何处,这都将起作用)


Ze *_*eus 5

如果将类组件转换为功能组件,自定义挂钩useFormikContext提供了一种在树下任意位置使用提交的方法:

   const { values, submitForm } = useFormikContext();
Run Code Online (Sandbox Code Playgroud)

PS:这仅适用于那些并不真正需要在Formik组件外部调用submit的人,因此您可以将组件放在组件树中的更高级别,而不是使用ref Formik,并使用自定义钩子useFormikContext,但如果确实需要从外部提交,Formik您必须使用useRef .

   const { values, submitForm } = useFormikContext();
Run Code Online (Sandbox Code Playgroud)

https://formik.org/docs/api/useFormikContext

  • 问题是一个6字的句子。一是“外”。您如何认为这是正确答案? (11认同)

ABC*_*.ca 5

2021 年,使用 16.13.1,这种方式满足了我的几个要求:

\n
    \n
  • 提交/重置按钮不能嵌套在<Formik>元素内。请注意,如果您可以做到这一点,那么您应该使用答案useFormikContext,因为它比我的简单。(我的将允许您更改正在提交的表单(我有一个应用程序栏,但用户可以导航到多个表单)。
  • \n
  • 外部提交/重置按钮必须能够提交和重置 Formik 表单。
  • \n
  • 外部提交/重置按钮必须显示为禁用状态,直到表单变脏为止(外部组件必须能够观察 Formik 表单的dirty状态。)
  • \n
\n

这就是我的想法:我创建了一个新的上下文提供程序,专门用于保存一些有用的 Formik 内容,以链接我的两个外部组件,这些组件位于应用程序的不同嵌套分支中(全局应用程序栏和其他地方的表单,更深层次)事实上,在页面视图 \xe2\x80\x93 中,我需要提交/重置按钮来适应用户导航到的不同表单,而不仅仅是一个;不仅仅是一个<Formik>元素,而是一次只有一个)。

\n

以下示例使用 TypeScript,但如果您只知道 javascript,请忽略冒号后面的内容,这在 JS 中是相同的。

\n

您将<FormContextProvider>应用程序放置在足够高的位置,以使其包含需要访问 Formik 内容的两个不同组件。简化示例:

\n
<FormContextProvider>\n  <MyAppBar />\n  <MyPageWithAForm />\n</FormContextProvider>\n
Run Code Online (Sandbox Code Playgroud)\n

这是 FormContextProvider:

\n
<FormContextProvider>\n  <MyAppBar />\n  <MyPageWithAForm />\n</FormContextProvider>\n
Run Code Online (Sandbox Code Playgroud)\n

在呈现元素的组件中<Formik>,我添加了这一行:

\n
import React, { MutableRefObject, useRef, useState } from \'react\'\nimport { FormikProps, FormikValues } from \'formik\'\n\nexport interface ContextProps {\n  formikFormRef: MutableRefObject<FormikProps<FormikValues>>\n  forceUpdate: () => void\n}\n\n/**\n * Used to connect up buttons in the AppBar to a Formik form elsewhere in the app\n */\nexport const FormContext = React.createContext<Partial<ContextProps>>({})\n\n// https://github.com/deeppatel234/react-context-devtool\nFormContext.displayName = \'FormContext\'\n\ninterface ProviderProps {}\n\nexport const FormContextProvider: React.FC<ProviderProps> = ({ children }) => {\n  // Note, can\'t add specific TS form values to useRef here because the form will change from page to page.\n  const formikFormRef = useRef<FormikProps<FormikValues>>(null)\n  const [refresher, setRefresher] = useState<number>(0)\n\n  const store: ContextProps = {\n    formikFormRef,\n    // workaround to allow components to observe the ref changes like formikFormRef.current.dirty\n    forceUpdate: () => setRefresher(refresher + 1),\n  }\n\n  return <FormContext.Provider value={store}>{children}</FormContext.Provider>\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在同一组件中,我将此属性添加到<Formik>元素中:

\n
const { formikFormRef } = useContext(FormContext)\n
Run Code Online (Sandbox Code Playgroud)\n

在同一个组件中,<Formik>元素下嵌套的第一个东西就是 this(重要的是,注意添加了这一<FormContextRefreshConduit />行)。

\n
<Formik\n  innerRef={formikFormRef}\n  initialValues={initialValues}\n  ...\n>\n  {({ submitForm, isSubmitting, initialValues, values, setErrors, errors, resetForm, dirty }) => (\n    <Form>\n      <FormContextRefreshConduit />\n      ...\n
Run Code Online (Sandbox Code Playgroud)\n

在包含提交/重置按钮的组件中,我有以下内容。注意使用formikFormRef

\n
export const MyAppBar: React.FC<Props> = ({}) => {\n  const { formikFormRef } = useContext(FormContext)\n  \n  const dirty = formikFormRef.current?.dirty\n\n  return (\n    <>\n      <AppButton\n        onClick={formikFormRef.current?.resetForm}\n        disabled={!dirty}\n      >\n        Revert\n      </AppButton>\n      <AppButton\n        onClick={formikFormRef.current?.submitForm}\n        disabled={!dirty}\n      >\n        Save\n      </AppButton>\n    </>\n  )\n}\n
Run Code Online (Sandbox Code Playgroud)\n

对于调用 Formik 方法很有ref用,但通常无法观察其dirty属性(react 不会触发此更改的重新渲染)。FormContextRefreshConduit一起forceUpdate是一个可行的解决方法。

\n

谢谢,我从其他答案中获得了灵感,找到了一种满足我自己所有要求的方法。

\n