Is there anyway to have 2 submit buttons in 1 form?
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
<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?
Most helpful comment
initialValues={{ isSecondButton: false, }}That's the initial value you provide..
In Your renderProp