final-form / react-final-form

🏁 High performance subscription-based form state management for React
https://final-form.org/react
MIT License
7.39k stars 481 forks source link

`react-final-form` should be able to submit forms #332

Open jkantr opened 6 years ago

jkantr commented 6 years ago

So... bear with me for a minute. I had this absolutely crazy use case for a form library and I'm unsure how I got this way... so I'm sorry in advance.

I'd like to submit a form.

What is the current behavior?

react-final-form does not submit forms

https://github.com/final-form/react-final-form/blob/a651c117cb1bfe448dfb1c11ebafa614ca285121/src/ReactFinalForm.js#L106-L108

The handleSubmit fn currently prevents form submission in 100% of cases. You can just.. not call handleSubmit and pass the form instance to your own handler function, running form.submit() - but if it finds a form error - and you don't preventDefault(), then the form still submits (so you lose the validation-before-submit behavior). If you preventDefault after calling form.submit() that "works", in the sense that your react-final-form onSubmit prop fn is invoked... but with no event or any way to complete the action. So I'm kind of at a loss. Only thing I can think of is manually running validation and then just letting the event complete / invoking form.submit(), but at that point you lose a lot of the libraries' utility.

What is the expected behavior?

react-final-form should be able to submit a form.

The rest of the features work great - and I really like the design of the library. I just.. would like to be able to submit a form. I get that it's a bit more common use case to do some asynchronous thing or make a fetch request - but there are valid use cases for want to just make a post request. I assumed I was just missing something but I couldn't even find an escape hatch. The event itself seems to be kept pretty inaccessible. I'll take any advice (or submit a PR if you have a suggestion for how to implement the functionality)

jkantr commented 6 years ago

heh, so this uh.. "works":

  onSubmit = () => {
    this.state.submitEvent.target.submit();
  };

  handleIt = (e, form) => {
    e.persist();
    e.preventDefault();
    this.setState({ submitEvent: e }, () => {
      form.submit();
    });
  }

// down in the render fn ... 

<Form
  onSubmit={this.onSubmit}
  render={({ handleSubmit, form, submitting, pristine, values }) => {
    return (
       <form method='post' onSubmit={(e) => this.handleIt(e, form)}>
// ...

but... yeaahhhhh - it may just be my combined 6 hours of sleep over the last few days - i feel like i'm missing something obvious.

jkantr commented 6 years ago

So, for anyone happening upon this in the future... (shout out to @samsch for this solution):

render={({ handleSubmit, form, submitting, hasValidationErrors }) => (
    <form method='post' onSubmit={e => handleSubmit(hasValidationErrors && e)}>

Before this is closed.... might it be considered to add an option to make this a bit more ergonomic? the behavior is really non-obvious. It's basically "if there are validation errors, don't give the event to the internal handleSubmit wrapper, but still call it". Maybe an allowDefault prop that makes the onSubmit handler optional and just lets the form submit via the browser?

levino commented 4 years ago

I do not understand the solution? What does the onSubmit prop of the Form component look like?

khaphannm commented 4 years ago

@jkantr if u want to complete the action after submit, prevent from still submitting issue, use can do this way:

const onSubmit = async (values, api, complete) => {
      // Validation
      if(!values.groupId) {
        toast.error('Error message');
        complete(); //Done, not submitting any more
        return; // Return or throw error
}

And just pass onSubmit to the Form component normally:

<Form
      onSubmit={onSubmit}
/>