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

Add a nicer API to Perform Form actions outside of render #551

Open dawidcxx opened 5 years ago

dawidcxx commented 5 years ago

Are you submitting a bug report or a feature request?

feature request

What is the current behavior?

Currently you have to stuff that feels hacky https://github.com/final-form/react-final-form/blob/master/docs/faq.md#how-can-i-trigger-a-submit-from-outside-my-form

With hooks out there I think we could get a better, first class API. It could look like this

function TestForm() {
    // ... 
    const formApi = useFormApiRef();
    return (
        <div>
            <Form
                apiRef={formApi}
                onSubmit={() => {/*...*/ }}
                render={() => <div />}
            />
            <button onClick={() => {
                formApi.reset();
            }}>outsider button</button>
        </div>
    );
}
  )
erikras commented 5 years ago

I'll consider this. You're not the first to request it. However, until/if it's implemented, it's not too difficult to do it oneself...

function TestForm() {
  const formRef = React.useRef()
  return (
    <div>
      <Form onSubmit={onSubmit}>
        {({ handleSubmit, form }) => {
          formRef.current = form
          return (
            <form onSubmit={handleSubmit}> ... fields ... </form>
          )
        }}
      </Form>
      <button onClick={() => formRef.current.reset()}>Reset</button>
    </div>
  )
}
Andarist commented 5 years ago

Not that I need it right now, but I remember using the proposed work around in my code in the past. This doesnt look good, but gets the job done. So I would be in favour of adding DI possibility for formApi.

erikras commented 5 years ago

Another possibility is that there's a recent API change #520 (that I just now remembered to document 😅) that lets you provide your own form instance to <Form>, so something like this could work:

import { createForm } from 'final-form'

function TestForm() {
  const formRef = React.useRef(createForm({
    onSubmit: myOnSubmit
  })
  return (
    <div>
      <Form form={formRef.current}>
        {({ handleSubmit, form }) => (
          <form onSubmit={handleSubmit}> ... fields ... </form>
        )}
      </Form>
      <button onClick={() => formRef.current.reset()}>Reset</button>
    </div>
  )
}
Andarist commented 5 years ago

Exactly the kind of an API I had in mind 👌

ayan-b commented 1 year ago

How can we use this in a class component?

Another possibility is that there's a recent API change #520 (that I just now remembered to document 😅) that lets you provide your own form instance to <Form>, so something like this could work:

import { createForm } from 'final-form'

function TestForm() {
  const formRef = React.useRef(createForm({
    onSubmit: myOnSubmit
  })
  return (
    <div>
      <Form form={formRef.current}>
        {({ handleSubmit, form }) => (
          <form onSubmit={handleSubmit}> ... fields ... </form>
        )}
      </Form>
      <button onClick={() => formRef.current.reset()}>Reset</button>
    </div>
  )
}