Formik: onChange for entire form

Created on 27 Nov 2017  ·  20Comments  ·  Source: formium/formik

I have a form where some field's values depend on other fields values. I have been using redux-form and have been able to calculate these values using an onChange handler on the top-level form object.

I don't see an equivalent using formik, is this right? I thought I could hijack the validation function to do this, but it's not passed the formik bag, only the values to validate.

Another usecase for a form-level onChange is automatic persistence without needing to hit a submit button e.g. something like:

<Formik
  initialValues={JSON.parse(localStorage.getItem('formvalues'))}
  onChange={(values) => {
    localStorage.setItem('formvalues', JSON.stringify(values))
  }}
>
{/* … */}
</Formik>

Another option could be a "submitOnChange` flag.

Duplicate

Most helpful comment

Sorry to reopen a closed thread, but this seemed like the most appropriate place to follow up on this.

If I understand correctly, the suggestion is to use another component to simulate onChange responses. I can see how that might make sense for something extremely agnostic like FormikPersist, but for the general situation where you wish to cause some effect based on form changes, this doesn't make sense to me.

Consider a simple form which contains dropdowns for the ingredients of a burger. You wish to render a preview of the burger _outside_ the form somewhere. It would seem fairly simple (and conventional) to do this by passing the form an "onBurgerIngredientsChanged" handler, and hooking that up into the form's onChange handler.

Am I correct in understand you would suggest instead creating a <BurgerIngredientsChangedDetector /> component, complete with constructor, componentDidMount, componentWillReceiveProps methods ...

It seems to me this would very naturally lead people to creating their own <OnFormUpdate hander={someHandler}/> component. In this case... why the extra complexity? Would it not be better to be able to pass Formik an onChange handler?

Just my two cents - I'm facing this problem now with a few forms that are slightly more dynamic than their siblings. Any thoughts would be much appreciated.

Thanks

Edit: Furthermore - and I think this is the bigger issue - by binding "onChange" type handlers into the render lifecycle, you're coupling two very different concepts. My form might render itself every second if it's being passed a readonly "counter" value - this doesn't mean any of the values have changed!

All 20 comments

You will always receive latest values in render prop. So you can perform anything you need there. I also recommend having a look at https://github.com/jaredpalmer/formik-persist how such functionality could be implemented. Take a note that <FormikPersist /> takes values from the context, you can of course pass it as prop instead.

@prichodko is exactly correct.

Sorry to reopen a closed thread, but this seemed like the most appropriate place to follow up on this.

If I understand correctly, the suggestion is to use another component to simulate onChange responses. I can see how that might make sense for something extremely agnostic like FormikPersist, but for the general situation where you wish to cause some effect based on form changes, this doesn't make sense to me.

Consider a simple form which contains dropdowns for the ingredients of a burger. You wish to render a preview of the burger _outside_ the form somewhere. It would seem fairly simple (and conventional) to do this by passing the form an "onBurgerIngredientsChanged" handler, and hooking that up into the form's onChange handler.

Am I correct in understand you would suggest instead creating a <BurgerIngredientsChangedDetector /> component, complete with constructor, componentDidMount, componentWillReceiveProps methods ...

It seems to me this would very naturally lead people to creating their own <OnFormUpdate hander={someHandler}/> component. In this case... why the extra complexity? Would it not be better to be able to pass Formik an onChange handler?

Just my two cents - I'm facing this problem now with a few forms that are slightly more dynamic than their siblings. Any thoughts would be much appreciated.

Thanks

Edit: Furthermore - and I think this is the bigger issue - by binding "onChange" type handlers into the render lifecycle, you're coupling two very different concepts. My form might render itself every second if it's being passed a readonly "counter" value - this doesn't mean any of the values have changed!

In case someone stumbles upon this thread, I've written a lib for this use case: https://github.com/ramitos/formik-observer

Hey,
I have been using Formik on 2 production projects now and I have needed this feature for both. At the moment I hijacked the validation method to trigger the onChange. I feel like this would be worth considering as a built in feature?
You can't trigger side-effects in render() so this is required if you want to trigger any kind of side-effect. An onChange callback would just avoid user to have to build a special library for every single side-effect.

These side-effects could be:

  • Persist the form state in a local storage (supported by formik-persist)
  • Save the form state remotely via an API, or just call any API on form change
  • Display a UI element outside of the form (for example a form progress bar in a menu)

Also notice how these usages are actually the only reason why one would use Redux-Form over Formik. Once you have that onChange callback, you can really easily save the state in Redux if you want to, which nullify the need of using Redux-Form and allow all the usages I said above.

See #401

Not something I'm proud of, but I hooked into the validate prop to get access to the form values on every change 😏

// Rough sketch

class Component {

  validate = values => {
    // Do what you want with the values
    console.log(values)
    return {}
  }

  render() {
    <Formik validate={this.validate} />
  }
}

⚠️ I wouldn't recommend, but it might save some people that are in a pinch. Use my creative hacks at your own risk.

@ramitos - Great little package, perfect for what I needed it for.
Thanks

Founded some interesting solution of this issue.
<Formik> {({ values }) => ( <Form> {this.handleWholeFormChange(values)} <Field /> </Form> )} </Formik>

@vsamotskiy What if I wanna update my component state in this.handleWholeFormChange(values)?

I can't believe why I can't use properly and simply the onChange property.

Added a notifyOnChange prop to formik. This should solve the form level onChange issue.
https://github.com/jaredpalmer/formik/pull/1397

PS: for now I have @brotzky 's creative hack in prod :)

Since notifyOnChange hasn't been merged yet, I did this little hack for observing formik values.

Component

import React, { useEffect } from 'react'

interface Props<T> {
  onChange: (value: T) => void
  value: T
}

export function FormikObserver<T>(props: Props<T>) {
  useEffect(() => {
    props.onChange(props.value)
  }, [Object.values(props.value).join(', ')])
  return null
}

FormikObserver.defaultProps = {
  onChange: () => null,
}

Usage

<Form>    
{(formProps: FormikProps<{}>) => {
    return (
       <Fragment>
         // Your form
         <FormikObserver value={formProps.values} onChange={value => console.log(value)} />
       </Fragment>
     )
   }}
</Form>

What about doing something like this? Which I think is what @prichodko was saying in
https://github.com/jaredpalmer/formik/issues/271#issuecomment-347142295

<Formik initialValues={initialValues} onSubmit={onSubmit}>
  {({ values }) => (
    <Form onChange={() => onFormChange(values)}>
      {/* Input fields */}
    </Form>
  )}
</Formik>

I just ignore the synthetic event that's piped into the <Form /> onChange event handler and replace it with the values dictionary from the <Formik /> value render prop. Works like a charm in my project 😄

Didn't need any Observable third party implementations.

@ebrearley: it doesn't work for React Native as there is no <Form /> component there.

My use case: I need to verify isValid after each change of all the pseudo-form's inputs and trigger an event outside Formik. In the render method isValid is not stable (!) The only place where it's stable is on the onChange event of one of the inputs. So the only solution for me was to listen for isValid in each onChange event of each input...

I think a better solution is more than needed. At least for React Native.

@dragosdehelean the proper way to handle something like this is to use an effect. An example of this implementation can be seen in:

I'm hoping to add some examples to the documentation in #1787. Let me know if this helps!

I don't agree that a separate component with an effect is the reasonable architecture. This is essentially diverting from the original (and reasonably sane) form configuration via props to configuration via XML.

You don't respond to the form submit event by adding a component to your form:

<Form>    
{(props) => (
    <>
        ...
        <SubmitObserver value={props.values} onSubmit={this.onSubmit} />
        ...
    </>
)}
</Form>

Why would change be different?

Logically both submit and change are simply one of many events that a form may emit, just like reset or validate.
I fail to see why different events should have so wildly different approaches in canonical use.

@Etheryte I absolutely agree with your argument that it's not intuitive. However, I do think a simple onChange wouldn't provide enough flexibility for all user scenarios, and if we don't find a way to provide for all user scenarios, there isn't a strong case to expand the API. Feel free to open a PR if you have a solution that can both optimize for renders when states are identical and allow users to circumvent this optimization if needed in a way that is clear to users. I haven't looked much into the v2 api so maybe there's a simple way now that we have hook support.

Edit: the "Form Submit Event" example doesn't really exist in React Native so it doesn't apply to the full audience of Formik.

Couldn't you just have two separate callbacks? onValuesChanged and onNewValidValues or something?

As this thread is still quite high up in the google search I post the unnecessary complex but new way here:
https://formik.org/docs/api/useFormikContext

Was this page helpful?
0 / 5 - 0 ratings