Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom "dirty" logic for Formik

I have an app made with React and Formik. I am trying to add a warning for users to save changes before navigating away. Starting with react-router-dom 5, we could use Prompt. Therefore, my initial code was:

const PromptWhenDirty = () => {
  const { dirty, touched } = useFormikContext();
  console.log('touched:', touched);
  return <Prompt when={dirty} message={'Leave without saving changes?'} />;
};

and I added one line to my formik based "step wizard" so now it looks like:

  <Form>
    <PromptWhenDirty />
    <Step
      current={currentStep}
      values={values}
      onChange={onChange}
      handleSubmit={handleSubmit}
      errors={errors}
      isSubmitting={isSubmitting}
    />
  </Form>

The Prompt dialog does show up, and it is working correctly. However, for some steps, my dirty flag is always set to true. I am unsure why exactly. I do utilize useField and a bunch of other features. I am assuming that dirty possibly somehow set during [some of] my steps' fields initialization.

Further, until I press submit once [-this is a step wizard where data gets resubmitted on each step], touched is always {}. Editing fields data does not set it. Once I submit the form for the first time, every field in my form is in touched and set to true.

I am fine with dirty just being set inside onChange as:

const onChange = (props) => {
  console.log('set dirty here');
  ...
}

I understand that I could use the state hook, and set it inside onChange. However, I am under the impression that mixing Formik with the state is a bad idea, hence useField was added.

Could I override how dirty is computed?

like image 817
Igor Shmukler Avatar asked Jun 10 '26 06:06

Igor Shmukler


1 Answers

I ended-up doing the below:

useEffect(() => {
  // initialization is compete, reset `dirty`
  if (mask.value) {
    resetForm({
      values: {
        ...values,
        mask: mask.value,
        croppedImage: croppedImage.value
      }
    });
  }
  // eslint-disable-next-line
}, [mask.value]);

It is not a perfect solution. Changes to the mask don't set dirty and worse reset it. Yet, it is significantly better than before. Plus, the hook is inside one of the steps. Other steps don't have initialization problems and do not require resetting dirty. I tried to hook-up to onReady method of my cropper component. However, in conjunction with formik useField, I could not easily get it to work.

like image 168
Igor Shmukler Avatar answered Jun 17 '26 14:06

Igor Shmukler



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!