Open kserjey opened 5 years ago
The answer to #214 was not to use a hack, but to perform an action before submit. Any form should only have 1 submit action. That action can take different actions depending on factors that occur before it. So if there are two buttons, one with a normal submit and one with a secondary action, one could wrap a callback which will split the decision beforehand:
const getSubmitCallback = saving =>
values => {
if (saving) {
doSave(values);
} else {
doSubmit(values);
};
};
const MyDualActionForm = () => {
const onSave = React.useCallback(getSubmitCallback(true), []);
const onSubmit = React.useCallback(getSubmitCallback(false), []);
return (
<Formik onSubmit={onSubmit}>
<input type="button" onClick={onSave} value="Save" />
<input type="submit" value="Submit">
</Formik>
);
}
Or one could use a class and do:
onClick={() => this.setState({saving: true}, formikProps.onSubmit)}
where onSubmit is now a callback that occurs after the state has been updated.
I understand, but I think it could be simpler if there was an option to pass submit context. In my case I have two submit buttons. One of them to save item as draft and the other to save item as it is. So I have two handlers for two buttons. They looks similar:
withHandlers({
handleSubmitDraft: ({ submitForm, setLifeState }) => {
setLifeState('DRAFT');
setTimeout(submitForm);
},
handleSubmitActual: ({ submitForm, setLifeState }) => {
setLifeState('NORMAL');
setTimeout(submitForm);
}
})
And then, in submit handler I get lifeState
state and append it to rest fields:
withFormik({
handleSubmit: (values, { props }) => {
const item = { ...values, lifeState: props.lifeState };
createItem(item).then(() => { /* handle success submission */ });
}
})
And if there was a way to pass context, It would be simpler and doesn't require extra state:
withFormik({
handleSubmit: (values, { submitContext }) => {
const item = { ...values, ...submitContext });
createItem(item).then(() => { /* handle success submission */ });
}
}),
withHandlers({
handleSubmitDraft: ({ submitForm, setLifeState }) => {
submitForm({ lifeState: 'DRAFT' });
},
handleSubmitActual: ({ submitForm, setLifeState }) => {
submitForm({ lifeState: 'NORMAL' });
}
})
And if submission was called by handleSubmit
:
<button onClick={handleSubmit}>Save</button>
<button onClick={handleSubmit}>Save as Draft</button>
submitContext
could be a click event of a clicked button.
So all you're asking for is the submit event during onSubmit
? I don't have anything against that, necessarily, but I'm not the one to make the final for API changes. I'd recommend modifying this issue from "Submit Payload" to something like "Pass Submit Event to onSubmit Handler" to help future maintainers understand what is being asked for!
Yes, but also I would like to pass some data to submitForm
, like is done in third example. So in submit handler there would be an event (from handleSubmit
) or data passed via submitForm
.
So all you're asking for is the submit event during
onSubmit
?
YES ! I have exactly this problem right now. I want a "save as draft" and a "save & publish" button, very easy to handle, I would simply like to add a query parameter to the post URL, but I have been looking for any clean solution for the last 2 hours and can't find any.. Having an event helping me to find out what is the name of the submit button used would solve the case.
What I would like is being able to have 2 buttons like this
<button type="submit" name="save_draft">Save draft</button>
<button type="submit" name="save_and_publish">Save and publish</button>
And then in my <Formik>
instance, having a new form property like this
onSubmit = async (values, form) => {
// my saving ajax request
if (form.submittedButtonName === 'save_and_publish') {
publish();
}
}
@florentdestremau that's exactly what I was thinking. We have some very complex forms (legacy app unfortunately) with some having up to 6 submit actions. Think save, save & approve, save & create new, save & decline, etc...
We've been working around this for a few months, if everyone can agree on a pattern i can throw together a PR.
I also handle my two submit buttons use case with state:
onClick={() => this.setState({customFlag: true}, submitForm())
...and then read the flag inside handleSubmit()
.
FYI the particular scenario is that I want to redirect the user to another route if one of the two submit actions were called.
This works but is slightly suboptimal:
setState()
triggers unnecessary render (by default anyway)I entertained by the idea to save the flag with Formik's setStatus()
instead of using the state but it turns out you can't read status in handleSubmit()
:https://jaredpalmer.com/formik/docs/api/withFormik#the-formikbag @jaredpalmer that's probably for a reason?
All in all having the ability to pass a custom payload submitFom()
sounds like a great idea for a variety of valid use cases.
The answer to #214 was not to use a hack, but to perform an action before submit. Any form should only have 1 submit action. That action can take different actions depending on factors that occur before it. So if there are two buttons, one with a normal submit and one with a secondary action, one could wrap a callback which will split the decision beforehand:
const getSubmitCallback = saving => values => { if (saving) { doSave(values); } else { doSubmit(values); }; }; const MyDualActionForm = () => { const onSave = React.useCallback(getSubmitCallback(true), []); const onSubmit = React.useCallback(getSubmitCallback(false), []); return ( <Formik onSubmit={onSubmit}> <input type="button" onClick={onSave} value="Save" /> <input type="submit" value="Submit"> </Formik> ); }
Or one could use a class and do:
onClick={() => this.setState({saving: true}, formikProps.onSubmit)}
where onSubmit is now a callback that occurs after the state has been updated.
@johnrom Correct me if I am wrong, but the onSave
callback is not really a submit. That means that when you click onSave, the submission phases described in here won't run.
@hectorricardo that's true! in the case above, as a user I would probably expect that an autosave would save an invalid state. However, that might not always be the case. This issue is still open because passing context to onSubmit is probably a good idea. However, in the meantime it seems like a good time for a ref!
const getSubmitCallback = async savingRef =>
values => {
if (savingRef.current) {
await doSave(values);
savingRef.current = false;
} else {
await doSubmit(values);
};
};
const MyDualActionForm = () => {
const savingRef = React.useRef(false);
const onSubmit = React.useCallback(getSubmitCallback(savingRef), []);
return (
<Formik onSubmit={onSubmit}>
{({ handleSubmit }) => (
<>
<input type="button" onClick={() => {
savingRef.current = true;
handleSubmit();
}} value="Save" />
<input type="submit" value="Submit" />
</>
)}
</Formik>
);
}
🚀 Feature request
Current Behavior
People do hack with
setTimeout
+setFieldValue
- #214Desired Behavior
It would be great if we have an option to pass something with
submitForm(payload)
to submit handler or get an event from submitted button.Who does this impact? Who is this for?
For all people who need to render two submit buttons with different submitting logic!