I'm using React 18, Ionic 7, and Formik 2.4.3. I have a component that I use when I want to scroll to the top the location of an error
export const getFieldErrorNames = (formikErrors) => {
const transformObjectToDotNotation = (obj, prefix = '', result = []) => {
Object.keys(obj).forEach((key) => {
const value = obj[key]
if (!value) return
const nextKey = prefix ? `${prefix}.${key}` : key
if (typeof value === 'object') {
transformObjectToDotNotation(value, nextKey, result)
} else {
result.push(nextKey)
}
})
return result
}
return transformObjectToDotNotation(formikErrors)
}
const ScrollToFieldError = ({ submitCount, errors }) => {
useEffect(() => {
const fieldErrorNames = getFieldErrorNames(errors)
if (fieldErrorNames.length <= 0) return
const elements = document.getElementsByName(fieldErrorNames[0])
const element = elements[0]
if (!element) return
// Scroll to first known error into view
element.scrollIntoView({ behavior: 'smooth', block: 'center' })
}, [submitCount])
return null
}
export default ScrollToFieldError
In my Formik form, I use the component like so
const validationSchema = Yup.object().shape({
selectedIds: Yup.array().min(1, 'Select at least one item').required('Select at least one item')
})
...
<Formik
enableReinitialize={true}
initialValues={{ selectedIds }}
validationSchema={validationSchema}
onSubmit={(values) => {
handleSaveAndContinue(values.selectedIds)
}}
>
{(formikProps) => (
<form onSubmit={formikProps.handleSubmit}>
<ScrollToFirstError submitCount={formikProps.submitCount} errors={formikProps.errors} />
{formikProps.touched.selectedIds && formikProps.errors.selectedIds ? (
<div>{formikProps.errors.selectedIds}</div>
) : null}
<IonList>
...
{items.map((item) => (
<IonItem key={item.id}>
<IonCheckbox
name='selectedIds'
value={item.id}
>{`${item.name}`}</IonCheckbox>
</IonItem>
))}
</IonList>
<IonButton aria-lael='Save And COntinue' type='submit'>
Submit
</IonButton>
</form>
)}
</Formik>
The issue is when I click my "Submit" button without checking anything, although my error message properly appears, the scroll to the top of the page does not occur. I would like to keep my component, ScrollToFirstError, as generic as possible as I use it on other types of forms, so curious if there's a way to adjust my existing form to get the functionality to work. An example with the problem is here -- https://stackblitz.com/edit/an5yjh-c2pz8v?file=src%2Fcomponents%2FScrollToFirstError.tsx,src%2Fcomponents%2FMyForm.tsx,package-lock.json
It seems that the values of formikProps.submitCount
and formikProps.errors
are updated separately. Check the process here.
They don't update at the same time. The useEffect of ScrollToFieldError
only has the dependency of submitCount
and not for errors
. So when it detects a change for submit, the error array isn't updated yet. Adding errors
in dependency will fix your issue.
const ScrollToFieldError = ({ submitCount, errors }) => {
useEffect(() => {
const fieldErrorNames = getFieldErrorNames(errors);
if (fieldErrorNames.length <= 0) return;
const elements = document.getElementsByName(fieldErrorNames[0]);
const element = elements[0];
if (!element) return;
// Scroll to first known error into view
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
}, [submitCount, errors]);
return null;
};