45
votes

Current Behavior

<Formik
    isInitialValid
    initialValues={{ first_name: 'Test', email: '[email protected]' }}
    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>
        );
    }}
/>

I'm trying this solution https://github.com/jaredpalmer/formik/issues/73#issuecomment-317169770 but it always return me Uncaught TypeError: _this.props.onSubmit is not a function

When I tried to console.log(this.form) there is submitForm function.

Any solution guys?


- Formik Version: latest - React Version: v16 - OS: Mac OS

9

9 Answers

66
votes

Just for anyone wondering what's the solution via React hooks :

Formik 2.x, as explained in this answer

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

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

const handleSubmit = () => {
  submitForm();
}

Keep in mind that solution only works for components inside a Formik component as this uses the context API. If for some reason you'd like to manually submit from an external component, or from the component the Formik is actually used from, you can actually still use the innerRef prop.

TLDR ; This context answers works like a charm if the component that you're submitting from is a child of a <Formik> or withFormik() component, otherwise, use the innerRef answer below.

Formik 1.5.x+

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

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

// Render
<Formik innerRef={formRef} />
39
votes

You can bind formikProps.submitForm (Formik's programatic submit) to a parent component and then trigger submission from the parent:

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;
7
votes

The best solution I've found is described here https://stackoverflow.com/a/53573760

Copying the answer here:

Add "id" attribute to your form: id='my-form'

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

Then add the same Id to the "form" attribute of the target button outside of the form:

<button form='my-form' type="submit">Outside Button</button>

Now, the 'Outside Button' button will be absolutely equivalent as if it is inside the form.

Note: This is not supported by IE11.

3
votes

I just had the same issue and found a very easy solution for it hope this helps:

The issue can be solved with plain html. If you put an id tag on your form then you can target it with your button using the button's form tag.

example:

      <button type="submit" form="form1">
        Save
      </button>
      <form onSubmit={handleSubmit} id="form1">
           ....
      </form>

You can place the form and the button anywhere even separate.

This button will then trigger the forms submit functionality and formik will capture that continue the process as usual. (as long as the form is rendered on screen while the button is rendered then this will work no matter where the form and the button are located)

2
votes

If you convert your class component to a functional component, the custom hook useFormikContext provide a way to use submit anywhere down the tree:

   const { values, submitForm } = useFormikContext();

PS: this only for those who don't really need to call submit outside the Formik Component, so instead of using a ref you can put your Formik component at a higher level in the component tree, and use the custom hook useFormikContext, but if really need to submit from outside Formik you'll have to use a useRef .

<Formik innerRef={formikRef} />

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

1
votes

If you're using withFormik, this worked for me:

  const handleSubmitThroughRef = () => {
    newFormRef.current.dispatchEvent(
      new Event("submit", { cancelable: true, bubbles: true })
    );
  };

Just put a regular react ref on your form:

  <form
        ref={newFormRef}
        
        onSubmit={handleSubmit}
      >
0
votes

found the culprit.

There are no longer onSubmitCallback on Formik props. Should change it to onSubmit

0
votes

You can try it

const submitForm = ({ values, setSubmitting }) =>{//...do something here}

<Formik onSubmit={(values, {setSubmitting })=> submitForm({values, setSubmitting})> {()=>(//...do something here)}