react-final-form icon indicating copy to clipboard operation
react-final-form copied to clipboard

forwardRef

Open viczam opened this issue 6 years ago • 15 comments

Bug report

It seems I can't set a ref anymore on <Form /> components.

I looked into <ReactFinalForm /> component and it doesn't seem to forward the ref.

Maybe using useImperativeHandle to expose the form api would be a solution?

viczam avatar May 18 '19 19:05 viczam

<Form> doesn't, itself, render any components, so I'm not sure what the ref would be too...?

erikras avatar May 29 '19 18:05 erikras

Would be nice to access the form api through a ref.

viczam avatar May 29 '19 19:05 viczam

Seems like you could do a formRef.current = form in your <Form>'s render prop (or a <FormSpy>)... Not gonna be set on first render, but would be from there on. 🤔

erikras avatar May 29 '19 20:05 erikras

It looks a bit hackish and limiting. Besides that, what if I don't have access to Form's children? I can probably submit a PR myself toward the end of the week and maybe present some actual use cases if you're ok with that.

viczam avatar May 29 '19 21:05 viczam

How about something like this:

const MyForm = () => {
  const form = React.useRef()
  return (
    <Form onSubmit={onSubmit} formRef={form}>
      ...
    </Form>
}

Where the form element will be courteous enough to pass you back the form instance. About what you're looking for?

erikras avatar Jun 01 '19 09:06 erikras

That's exactly what I was looking for. Except that I think it's enough to use the ref property and not have a custom formRef property for that. This can probably be achieved with something like this (in ReactFinalForm.js):

const ReactFinalForm = forwardRef(({...}, ref) => {
  const form: FormApi = useConstant(() => {
    const f = createForm(config)
    f.pauseValidation()
    return f
  })

  React.useImperativeHandle(ref, () => form);
});

viczam avatar Jun 01 '19 19:06 viczam

I have dozens of components that use this:

<Form
        ....
        ref={el => (this.form = el)}
/>

And since upgrading to v6 of react-final-form I'm getting this Warning everywhere:

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

crobinson42 avatar Jun 06 '19 20:06 crobinson42

FYI - this is a breaking change that needs to be fixed in a patch version ASAP.

In v5 this works:

<Form
  onSubmit={() => this.form.form.reset()}
  ref={el => (this.form = el)}
/>

and no longer works in v6 due to the ref not being forwarded by react-final-form

crobinson42 avatar Jun 06 '19 20:06 crobinson42

Any status on this? I can add a PR for forwardRef if help is wanted

ChrisWiles avatar Dec 06 '19 16:12 ChrisWiles

@ChrisWiles yes please do, help is required here https://github.com/final-form/react-final-form/pull/608#issuecomment-525540956 but someone with flow experience needs to get into it.

Please add forwardRef to react-final-form it's a must have.

For react-native, it gives the worst user experience without it as we can't jump from a field to another using the NEXT button, see https://github.com/final-form/react-final-form/issues/779

kopax avatar Apr 08 '20 15:04 kopax

Hi guys, that's a big issue here, is there any chance that someone with flow experience solves #608 ?

Without flow experience, how can we help this to move forward?

kopax avatar May 20 '20 19:05 kopax

v6.5.0 as been released with the merge : thanks for the release ! I would like to know if anyone have tested it ? Passing const ref = useRef(); to the <Field ref={ref} /> does not save the ref as I expect.

I also tried createRef(), both did return a current with value undefined.

kopax avatar May 28 '20 17:05 kopax

I think this would be great. Having let in your component or manually mutating ref.current feels wrong and always raises eyebrows in our code reviews when someone needs the FormApi outside the component (i.e. an external submit button which we have in TONS of places in our React Native app such as on the menu bar).

@viczam 's solution seems the most correct, React way to do this:

const ReactFinalForm = forwardRef(({...}, ref) => {
  const form: FormApi = useConstant(() => {
    const f = createForm(config)
    f.pauseValidation()
    return f
  })

  React.useImperativeHandle(ref, () => form);
});

This would also make usage more inline with the ref on Field as well!

Are there strong arguments against just doing it this way?

vincentjames501 avatar Aug 18 '20 00:08 vincentjames501

How is this issue still open? It seems wild that in 2023 you still can't:

const formRef = useRef();

return (
  <Form ref={formRef} ...>
    {...}
  </Form>
);

And get a ref to the form object. We've had to introduce a silly wrapper component to achieve what seems like a very simple bit of functionality that should be restored to the core library.

wkirby avatar Dec 15 '23 15:12 wkirby