Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Form callback API #989

Open
crutchcorn opened this issue Oct 22, 2024 · 5 comments
Open

[FEATURE] Form callback API #989

crutchcorn opened this issue Oct 22, 2024 · 5 comments
Labels

Comments

@crutchcorn
Copy link
Member

Imagine a usecase where the user wants two buttons, they both send the item to the backend (after form validation) but one goes back to the list and the other goes to a different page, like a step 2 or a detail page.

To solve this, we're thinking of introducing a new API called onSubmitMeta that allows you to pass arguments to the onSubmit function via a meta field.

This API might look something like this:

  const form = useForm({
    defaultValues: {
      firstName: '',
      lastName: '',
    },
    // Is 'never' by default unless you provide a type
    // {} is the default value passed to `onSubmit`'s `meta` property
    onSubmitMeta: {} as { someValue: string },
    onSubmit: async ({ value, meta }) => {
      // Do something with form data
      console.log(value)
      // Do something with the values passed via handleSubmit
      console.log(meta)
    },
  })

  return (
    <div>
      <h1>Simple Form Example</h1>
      <form
        onSubmit={(e) => {
          e.preventDefault()
          e.stopPropagation()
          form.handleSubmit({
            someValue: 'value',
          })
        }}
      >

By having onSubmitMeta it enables us to have a few features on top of passing properties such as:

  • Type safety
  • Default arguments to meta field from the FW itself

This API should be purely additive, so it may not make it into v1 of TanStack Form

@thejasviES
Copy link

Hi @crutchcorn can I be assigned this ticket?

@zoodirector
Copy link

Hey @crutchcorn . Thank you very much for taking up my ideas of #949 ! However, I don't find this API proposal of yours particularly advantageous. It would require me to put the business logic that handles the server response (e.g. navigating to different routes after submissions) inside the onSubmit function of the form. It is an inversion of control where not necessary. For me it would feel more natural if the form API would return control after the successful submission of the form and not wrap around the business logic that follows afterwards. I think that my suggested design in #949 would allow for a better decoupling of code. For example if I want to reuse the same redirection logic in different forms.

But that is just my opinion. I guess I could also live with the API suggested above.

@godd9170
Copy link

godd9170 commented Nov 5, 2024

I've got this exact use case, and I was able to solve it by looking at the id of the submitting button.

() => {
  let form = useForm({
    defaultValues: {},
    onSubmit: async ({ value }) => await post(value),
  });
  return (
    <form
      onSubmit={async (event) => {
        event.preventDefault();
        event.stopPropagation();
        await form.handleSubmit();
        if (event.nativeEvent.submitter.id === "save-and-go-back") {
          // redirect
        }
      }}
    >
      <button id="save" name="save" type="submit">
        Save
      </button>
      <button id="save-and-go-back" name="save-and-go-back" type="submit">
        Save & Go Back
      </button>
    </form>
  );
};

What I would love, is the ability to get the response returned from my onSubmit, from form.handleSubmit as @zoodirector has proposed above. Like this . That way, I could use some server values in the redirect (such as a newly created id).

So it would become

() => {
  let form = useForm({
    defaultValues: {},
    onSubmit: async ({ value }) => await post(value),
  });
  return (
    <form
      onSubmit={async (event) => {
        event.preventDefault();
        event.stopPropagation();
        const response = await form.handleSubmit();
        if (event.nativeEvent.submitter.id === "save-and-go-back") {
          history.push(`items/${response.id}`)
        }
      }}
    >
      <button id="save" name="save" type="submit">
        Save
      </button>
      <button id="save-and-go-back" name="save-and-go-back" type="submit">
        Save & Go Back
      </button>
    </form>
  );
};

@Rinaldo
Copy link

Rinaldo commented Nov 13, 2024

I think there's value in getting the result from onSubmit but I also see not wanting to deal with unhandled promise rejections when calling handleSubmit.

One possibility would be to move closer to the useMutation hook from TanStack Query where there are separate mutation.mutate and mutation.mutateAsync methods. The form object could have both submit and submitAsync methods which return void and the result of onSubmit respectively.

@crutchcorn
Copy link
Member Author

@zoodirector, can you help me understand (in psuedo-code) what a real-world example of what you'd want to build is?

I'm still not seeing the differences between your suggestion and our design here with the exception of typesafety

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants