Formik: How do you listen for changes to a formik field?

Created on 13 Nov 2018  路  8Comments  路  Source: formium/formik

鉂換uestion

I have a UI that I want to respond to the selected values of a formik form. It's a pricing UI where selecting different options results in a different total price. I need to be able to connect a listener for when a formik field has setFieldValue() called on it. I don't see such a function in formik so the way I've gotten around it is to duplicate the form schema as values in the state and every time I call setFieldValue() I also mutate the corresponding state value in the same way.

Most helpful comment

I recently needed to subscribe on some value. My input was editable with handleChange and also manually with setFieldValue. So to avoid multiple listeners, I just used hook like this:

    useEffect(() => {
        if (!isSanitized(values.field)) {
            setFieldValue('radius', sanitize(values.field));
        } else {
            action(values.field);
        }
    }, [values.field]);

Cheers

All 8 comments

Can you just move this logic to your render function and determine price/what fields to show based on values?

I need the logic in the render function but I also need it in the handleSubmit() of the formik object so I wrapped the formik object in another component. My code looks like this

const MyForm = props => {
  const {
    values,
    touched,
    errors,
    handleChange,
    handleBlur,
    handleSubmit,
    getPrice,
  } = props;
  return (
    {getPrice()}
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        onChange={handleChange}
        onBlur={handleBlur}
        value={values.name}
        name="name"
      />
      {errors.name && touched.name && <div id="feedback">{errors.name}</div>}
      <button type="submit">Submit</button>
    </form>
  );
};

const MyEnhancedForm = withFormik({
  mapPropsToValues: () => ({ name: '' }),
  handleSubmit: (values, { props, setSubmitting }) => {
    // need to use getPrice()
    sessionStorage.setItem('price', props.getPrice());
    setSubmitting(false);
  },
})(MyForm);

class MyEnhancedFormContainer extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      mirroredName: null,
    };
  }

  getPrice = () => {
    // does something with this.state.mirroredName
  }

  render() {
    <MyEnhancedForm
      getPrice={this.getPrice}
    />
  }
}

I recently needed to subscribe on some value. My input was editable with handleChange and also manually with setFieldValue. So to avoid multiple listeners, I just used hook like this:

    useEffect(() => {
        if (!isSanitized(values.field)) {
            setFieldValue('radius', sanitize(values.field));
        } else {
            action(values.field);
        }
    }, [values.field]);

Cheers

@lkostrowski In your example, what is action(values.field); doing? I think this may work for my issue... Using recaptcha, if the user waits too long, the value expires. I need to listen for the expiration and simply set the values.recaptcha to blank if it's not a valid token.

It does whatever you like. In my case it's only listening to formik state instead listening to many sources, so I sanitize value if is not. Then, value changes, so hook runs again, but else is running with my action. Don't remember what it was in this example, probably some redux dispatch

Thank you... I was curious if it was dispatching another Formik action. I ended up using your example to handle the recaptcha timeout and it works great.

// if recaptcha times out, an ugly error message is returned
// set the value from null to '' to get the validationSchema message
useEffect(() => {
    if (form.values.recaptcha === null) {
        form.setFieldValue('recaptcha', '')
    }
}, [form.values.recaptcha])

And what if you're working with class components? My initial thought is componentDidUpdate, but the formik values are neither in state, nor props. isSubmitting is close, but not quite what I need.

I came up with this approach:

const HandleFieldChange = connect(
  ({
    name,
    formik: {
      values: { [name]: value },
    },
    onChange,
  }) => {
    useEffect(() => {
      onChange(value);
    }, [onChange, value]);
    return null;
  }
);

That you would use inside a form like this:

<HandleFieldChange
    name="field-name"
    onChange={value => console.log(value)}
/>
Was this page helpful?
0 / 5 - 0 ratings

Related issues

green-pickle picture green-pickle  路  3Comments

Jucesr picture Jucesr  路  3Comments

outaTiME picture outaTiME  路  3Comments

ancashoria picture ancashoria  路  3Comments

dearcodes picture dearcodes  路  3Comments