Formik: 2 submit buttons

Created on 20 Oct 2017  路  34Comments  路  Source: formium/formik

Is there anyway to have 2 submit buttons in 1 form?

Most helpful comment

initialValues={{ isSecondButton: false, }}

That's the initial value you provide..

 onSubmit={values => {
  if(values.isSecondButton){
    do some action
   } else {
    do some other action
  }
 }

In Your renderProp

   <Button
        id="second-button"
        onClick={(e)=>{
            setFieldValue('isSecondButton',true)
             handleSubmit(e);
          }}
      >

All 34 comments

It semantically correct to have 2 submit buttons in 1 form?

Hi @cloud-walker,
In my case, I want to have a save button and submit button both use the same data/form but implement differently at the end. Is there anyway to work around this use case?

I have a similar case and for now I've done like that:

<button type="submit">pay with credit card</button>
<button onClick={() => {doStuff(); submitForm()}}>pay with paypal</button>

submitForm comes from Formik, but its not documented, maybe @jaredpalmer may enlighten us on this

I want to validate the form before submit the form... If I do via onClick then it's not going called handleSubmit()

if you call submitForm() validations will be called.

@cloud-walker has it right.

@jaredpalmer is there a way to pass a flag to the handleSubmit action from the button and get it in the onSubmit function. I've the similar situation where I've two actions for the same form.

Has anyone found a way to pass the flag that @pavanmehta91 requested?

I've found a way I set a flag and use setValue and then execute the handleClick function. So on submit I get which button was clicked. It's plain JS 馃憤

If anybody wants the code I can share it here.

@pavanmehta91 yes, please 馃榾

initialValues={{ isSecondButton: false, }}

That's the initial value you provide..

 onSubmit={values => {
  if(values.isSecondButton){
    do some action
   } else {
    do some other action
  }
 }

In Your renderProp

   <Button
        id="second-button"
        onClick={(e)=>{
            setFieldValue('isSecondButton',true)
             handleSubmit(e);
          }}
      >

@pavanmehta91
setFieldValue is async function because it updates a state where are all the values stored therefore it's not guaranteed that there will be the most actual values in onSubmit handler..

It is a shame that setFieldValue isn't a Promise or doesn't have a callback

@Bundas You're right and it should return a promise. But from my experience I've never faced an issue. We could also use setTimeout(()=> onSumbit(e),0). That'd run on the next tick.

You can do something like

<Button
   text='randomText'
   onClick={(e) => {
      props.values.foo = true
      props.handleSubmit()
   }}
/>

and in your submit function validate foo like

onsubmit (values) {
   if foo doSomething()
   else doSomethingElse()
}

@GonzalezAVictor You should not mutate the props

You should pass async function as onClick callback:

<Button
  onClick={async () => {
    await setFieldValue('isSecondButton', true);
    submitForm();}
  }
/>

@Funkyskank setfieldvalue is not a promise, so await wouldn't do anything here. this is the main issue here, that its not a promise :P

Also, if you want different behavior on your first submit button only onClick isn't going to cut it as you can trigger that button by pressing enter in your form as well.

But without await form won't submit at all.

And what the proper way to pass params to your submit handler?

Right now there isn't one without doing some settimeout hack.

There is now way to do it with vanilla JS so you must come with your own solution. Formik has no way to support it in current state.

What I do, if I remember correctly is this...

class Sample extends React.Component {
  submitAction = undefined;

  render() {
    return;
    <Formik
      onSubmit={values => {
        if (this.submitAction === 'a') {
          // ...
        }

        if (this.submitAction === 'b') {
          // ...
        }

        this.submitAction = undefined;
      }}
    >
      {formikProps => (
        <React.Fragment>
          <Form>
            <button
              type="button"
              onClick={() => {
                this.submitAction = 'a';
                formikProps.submitForm();
              }}
            >
              Action A
            </button>
            <button
              type="button"
              onClick={() => {
                this.submitAction = 'b';
                formikProps.submitForm();
              }}
            >
              Action B
            </button>
          </Form>
        </React.Fragment>
      )}
    </Formik>;
  }
}

@Andreyco Where do you get formikProps from. I can't seem to access it, I've seen FormikProps but not formikProps... capital and small letter f

I don't have to call submitForm() or handleSubmit() if i setFieldValue and call functions correspondingly in onSubmit and it works well.

Though i wonder how we should control the submit behaviour when pressing enter key.

@Funkyskank @rovansteen you need to set the third parameter of setFieldValue to false

from the doc

<Formik onSubmit={(values, { setSubmitting }) => {this.handleSave(values)}} >
{ ({setFieldValue, handleSubmit}) => (    
    <Form onSubmit={handleSubmit}>

        <Button type='button' onClick={() => {
            setFieldValue('publish', 1, false)
            handleSubmit()
        }}>Save and Publish</Button>

        <Button type='button' onClick={() => {
            setFieldValue('publish', 0, false)
            handleSubmit()
        }}>Save as draft</Button>

    </Form>
) }
</Formik>

I use this approach:

function FormContainer(props) {
  const [formType, setFormType] = useState(DEFAULT_FORM_TYPE);

  const handlerSubmit = (methodX, methodY) => async values => {
    if (formType === FORM_TYPE_X) {
      return await methodX(values);
    }
    return await methodY(values);
  };

  const submitFormType = (formTypeSubmited, handleSumitType) => () => {
    setFormType(formTypeSubmited, () => handlerSubmit());
  };

  return (
    <Formik
      onSubmit={handlerSubmit(props.saveToApiMethodX, props.saveToApiMethodY)}
      validate={values => props.validate(formType, values)}
    >
      {(values, handleSubmit, errors) => (
        <>
        <button onClick={submitFormType(FORM_TYPE_X, handlerSubmit)}>
        Method X
        </button>
        <button onClick={submitFormType(FORM_TYPE_Y, handlerSubmit)}>
        Method Y
        </button>
        </>
        )}
    </Formik>
  );
}

@cloud-walker
Thanks for your snippet. any idea how to pass onclick dosfuff() from div to formik tag?, just like onSubmit? or formik accepts only predefined functions?

Most likely do not want to write any setFieldValue() stuff for majority of use cases. Among other reasons that would imply having some (hidden) field just for the sake of setting a flag for the (second) submit button to perform whatever the custom action is, e. g. the save / publish use case.

There is only one viable solution by @cloud-walker:
<button type="button" onClick={() => {doStuff(); submitForm()}}>pay with paypal</button>

...where doStuff() is most likely going to be a setting custom state flag as detailed by @skoch-tf.

There is a PR #1792 which would remove the need to store anything in the state.

I use this approach:

function FormContainer(props) {
  const [formType, setFormType] = useState(DEFAULT_FORM_TYPE);

  const handlerSubmit = (methodX, methodY) => async values => {
    if (formType === FORM_TYPE_X) {
      return await methodX(values);
    }
    return await methodY(values);
  };

  const submitFormType = (formTypeSubmited, handleSumitType) => () => {
    setFormType(formTypeSubmited, () => handlerSubmit());
  };

  return (
    <Formik
      onSubmit={handlerSubmit(props.saveToApiMethodX, props.saveToApiMethodY)}
      validate={values => props.validate(formType, values)}
    >
      {(values, handleSubmit, errors) => (
        <>
        <button onClick={submitFormType(FORM_TYPE_X, handlerSubmit)}>
        Method X
        </button>
        <button onClick={submitFormType(FORM_TYPE_Y, handlerSubmit)}>
        Method Y
        </button>
        </>
        )}
    </Formik>
  );
}

I am not sure if I understood this solution. Regarding this line: setFormType(formTypeSubmited, () => handlerSubmit());

1) Does setFormType accept two arguments? Are you using this?
2) () => handlerSubmit() ? I think this should be replaced by handleSumitType (assuming that my first question was answered as I expected)

You can access window.event in the onSubmit handler to see which button was clicked :)

Using window.event in the onSubmit handler as @vpontis described worked great for me. More specifically setting i.e. the name attribute on the different submit buttons and accessing it through window.event.submitter.name.

@vpontis @leonvdb that's only for browsers, not React Native I suspect.

@vpontis @leonvdb that's only for browsers, not React Native I suspect.

Ya, I haven't tried it in React Native.

Is event passed into onSubmit @leonvdb or are you accessing through window.event?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Jungwoo-An picture Jungwoo-An  路  3Comments

najisawas picture najisawas  路  3Comments

Jucesr picture Jucesr  路  3Comments

pmonty picture pmonty  路  3Comments

giulioambrogi picture giulioambrogi  路  3Comments