setValue cancels setError
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 |
I have same problem, not only with setFieldValue also with setFieldTouched.
They are undo changes of setErrors
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
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