jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.73k stars 2.77k forks source link

setValue cancels setError #3845

Open sm2017 opened 1 year ago

sm2017 commented 1 year ago

Bug report

I would like to integrate formik and react-dropzone. in this library we have a onDrop callback with acceptedFiles and fileRejections properties. So it make sense to call both helpers.setValue(acceptedFiles) and helpers.setError when we select multiple valid and invalid files

Current Behavior

unfortunately setValue cancels setError regardless of the order of call

Expected behavior

setValue and setError should be independent

Reproducible example

https://codesandbox.io/s/inspiring-gianmarco-tytzq7?file=/src/App.tsx

Please comment helpers.setValue('Value') and see the result as well

Suggested solution(s)

Additional context

Your environment

Software Version(s)
Formik 2.4.2
React 18.2.0
TypeScript Version 5.1.3
Browser Chrome
npm/Yarn npm 9.6.7
Operating System Windows
fakhamatia commented 12 months ago

I have same problem, not only with setFieldValue also with setFieldTouched. They are undo changes of setErrors

Charlygraphy23 commented 11 months ago

I guess They have set the error value respective with the fieldValue. If fieldValue changes then all the error will be gone. Looks like normal behavior to me

image

RosePinkDragon commented 9 months ago

edit: the below works because setFieldError/Value are async calls : currently i am doing this:

useEffect(() => {
    const errors = {};
    /**
     *  toVerifyList is an object with sub objects containing a regex and err message to test the values
     * const toVerifyList: {
     *   [x: string]: {
     *      regEx: string;
     *      errMsg: string;
     *  };
     * }
     */
    Object.keys(toVerifyList).forEach((item) => {
      const fieldValidation = toVerifyList[item];
      const fieldValue = values[item];

      if (fieldValue && !RegExp(fieldValidation.regEx).test(fieldValue.toString())) {
        errors[item] = fieldValidation.errMsg;
      }
    });

    // Update validation errors
    setValidationErrors(errors);
  }, [isValidating, values]);

  // Set error messages and touched state based on validationErrors
  useEffect(() => {
    Object.keys(validationErrors).forEach((item) => {
      setFieldTouched(item, true, false);
      setFieldError(item, validationErrors[item]);
    });
  }, [validationErrors, setFieldError, setFieldTouched]);

previously, I was handling this is using a setTimeout

   setTimeout(() => {
      setFieldTouched('item', true, false);
      setFieldError('item', 'errMsg');
    }, 100);

but both ways are bad ideas, as it causes the errors to be cleared an re-instated, causing layout shifts, which i handled with more UI stuff but this works