Formik: [v2] <Formik /> `ref` prop

Created on 12 Jun 2019  路  20Comments  路  Source: formium/formik

馃殌 Feature request

Current Behavior

Since switching <Formik /> to a function component if you try to attach a ref to it as such:

<Formik ref={myRef} /> 

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

Desired Behavior

The desired behaviour is for there to be no error and the ref to provide a set of APIs to programmatically interact with <Formik />.

Suggested Solution

To enable the functionality it seems that the hook useImperativeHandle will be needed to wrap <Formik/> and expose the available methods.

export const Formik = forwardRef((props, ref) => {
  const formikRef = useRef()
  useImperativeHandle(ref, () => ({
    submitForm: () => handleSumbit()
  })

  return (
    <FormikProvider ref={formikRef} value={formikbag}>
      {...}
    </FormikProvider>
  )
})

An example per the docs

Who does this impact? Who is this for?

This is for anyone who needs to implement any programmatic functionality on .

Describe alternatives you've considered

Use the prior version where is a class component.

Additional context

I was trying to implement the debounced autosave that was handled by the parent component, without the need for a specific component that is a child of .

Formik

Most helpful comment

You can get a ref to formikBag via innerRef prop.

const bagRef = useRef();

<Formik innerRef={bagRef} />

Note: this isn't a reference to the Formik component, but to the "bag" of formik tools. like handleSubmit et all. I think using ref prop was avoided so that in the future if there's a reason to add a real ref, we can.

All 20 comments

I guess this applies to withFormik aswell?

I have two forms similar to:
https://codesandbox.io/s/formik-multiple-form-in-one-submission-kbf1w?fontsize=14

I would like to make a reset button that runs formik.resetForm() on both forms.

I can get a reference to the withFormik react node, but not child which is the actual Formik react node (which I'm guessing provides the formikBag API I need).

Ideas?

Have you made any progress with this? I'm facing a similar problem.

I managed to get this to work by passing in the ref as a propery.
const formikFormRef = useRef(null); //in parent
<FormikForm formikFormRef={formikFormRef} ></FormikForm> // in parent

Then inside the form

const FormikForm = (withFormik({ enableReinitialize: true})((props, values) => {
  useImperativeHandle(props.formikFormRef, () => ({
    autoSave: () => {
      console.log('xxx');
    }
  }));

Now I can call this from the parent with:

  useEffect(() => {
        setInterval(() => {
          formikFormRef.current.autoSave()
        }, 3000);
      });
  });

Any updates?

Any updates on this, I can't upgrade to v2 because I have to reset some forms in my app using the reference from outside the form itself

PR's welcome.

I would like to be able to implement this, but I don't know how to fix the interface FormikConfig to handle the type of the ref using forwardRef :disappointed:

Do we really need this? While you can't do <Formik ref={myRef} /> passing it as <Formik myRef={myRef} /> works. Then you can subscribe to it using useImperativeHandle(props.myRef, () => {}) from within the form and call it from outside as myRef.current.fooBar(). See my previous post for full example.

Hey guys. Do you have any thoughts on @johnrom suggestion?
Please, check this PR: https://github.com/jaredpalmer/formik/pull/1972

How is this for a work-around? Our code is still on v1 and it works but I haven't tested with v2.

  render() {
    const { children, ...props } = this.props;

    return (
      <Formik {...props}>
        {formikProps => {
          this.formikProps = formikProps;
          return this.renderChildren(formikProps);
        }}
      </Formik>
    );
  }

Here is a workaround that works for me in v2.

  • I created a component; FormikWithRef
  • The component forwards all the props to the original Formik component.
  • It then exposes the formikProps using useImperativeHandle
// FormikWithRef.tsx

import React, {forwardRef, useImperativeHandle} from 'react';
import {Formik, FormikProps, FormikConfig} from 'formik';

function FormikWithRef(props: FormikConfig<any>, ref) {
    let _formikProps: FormikProps<any>;

    useImperativeHandle(ref, () => (_formikProps));

    return (
        <Formik {...props}>
            {(formikProps) => {
                _formikProps = formikProps;
                if (typeof props.children === 'function') {
                    return props.children(formikProps);
                }
                return props.children;
            }}
        </Formik>
    );
}

export default forwardRef(FormikWithRef)

Then I use the new component; FormikWithRef instead of Formik;

// LoginPage.tsx

import {useRef} from 'react';
import {FormikProps} from 'formik';

export default function LoginPage() {
    const myFormRef = useRef<FormikProps<any>>(null);

    return <FormikWithRef ref={myFormRef}>
        {({
              values,
              errors,
              touched,
              handleChange,
              handleBlur,
              handleSubmit,
              isSubmitting,
          }) => (
            <form onSubmit={handleSubmit}>
                // ...
            </form>
        )}
    </FormikWithRef>
}

What's the status of this? I see this was closed, and I can see useImperativeHandle in the codebase in 2.1.2, but I can't pass a ref into the component.

I get the TS error: Property 'ref' does not exist on type 'IntrinsicAttributes & FormikConfig<...>

You can get a ref to formikBag via innerRef prop.

const bagRef = useRef();

<Formik innerRef={bagRef} />

Note: this isn't a reference to the Formik component, but to the "bag" of formik tools. like handleSubmit et all. I think using ref prop was avoided so that in the future if there's a reason to add a real ref, we can.

To me this looks like an undocumented breaking change that should be included in "Migrating to v2" of docs.

It is also poorly typed, the innerRef is of type (instance: any) => void

@teotn it is quite poorly typed 馃憥 . We're discussing a real implementation here: #2208

@johnrom What would be the classes equivalent?

I'm trying with useRef but it seems that it is intended only for function hooks, and createRef with ref doesn't work either.

For Formik slug being "without tears", I already have dropped a lot :(

@diosney useRef is specific to functional "React hooks". functional components are generally considered preferable to classes in many scenarios except some very complex use cases that functional components cannot be used for. however, for backwards compatibility the class-based ref method is

class MyClass = {
  public constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return (
      <Formik innerRef={this.myRef} />
    );
  }
}

Note, besides using innerRef instead of ref, this has nothing to do with Formik, and those are just plain ol' React tears.

docs link

@johnrom Thanks for your answer and sorry for my grunt, but I have several rounds on this already and tested lot of things without making it working as it should, I'm really just about pulling my off my hair.

I already tried your solution with createRef, even repeated using constructor since I do not do it like that (using typescript sugar classes) but it keeping outputing:

{"current": null}

and any call to this.myRef.errors or this.myRef.current.errors or any other Formik "bag" property gives an error.

The only way I could get some partial success was by something like:

 {(formikProps) => {
                self.formSubmit  = formikProps.handleSubmit;
                self.formIsValid = formikProps.isValid;
                self.formErrors  = formikProps.errors;
                ....

but for some wicked reason it only works if I made some change in the code while developing and having Hot Reload enabled.

Have any other suggestions?

Really don't know why it is not working, I updated Formik to 2.1.4 from 2.1.1 and all remains the same.

I'm trying to use those values on a Button below and outside the Formik component.

@diosney if you open a new issue someone may be able to help you there. during issue creation, you should try and reproduce your issue in a CodeSandbox (link to the sandbox starter is in the new issue creation template). You might figure out where the issue is during reproduction (happens a lot!), or if you are able to reproduce we'll be able to look immediately and see where the problem is.

@johnrom OK, I understand, thanks for your time.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

najisawas picture najisawas  路  3Comments

ancashoria picture ancashoria  路  3Comments

pmonty picture pmonty  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments

outaTiME picture outaTiME  路  3Comments