formik icon indicating copy to clipboard operation
formik copied to clipboard

setValue cancels setError

Open sm2017 opened this issue 11 months ago • 3 comments

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

sm2017 avatar Jul 07 '23 17:07 sm2017

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

fakhamatia avatar Jul 12 '23 15:07 fakhamatia

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

Charlygraphy23 avatar Jul 17 '23 18:07 Charlygraphy23

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

RosePinkDragon avatar Oct 11 '23 05:10 RosePinkDragon