-
-
Save dphrag/4db3b453e02567a0bb52592679554a5b to your computer and use it in GitHub Desktop.
import React from 'react'; | |
import { connect } from 'formik'; | |
class ErrorFocus extends React.Component { | |
componentDidUpdate(prevProps) { | |
const { isSubmitting, isValidating, errors } = prevProps.formik; | |
const keys = Object.keys(errors); | |
if (keys.length > 0 && isSubmitting && !isValidating) { | |
const selector = `[id="${keys[0]}"]`; | |
const errorElement = document.querySelector(selector); | |
errorElement.focus(); | |
} | |
} | |
render() { | |
return null; | |
} | |
} | |
export default connect(ErrorFocus); |
Everyone who want's to fix bug, when it focus only in first form element, just paste this part of code const selector =
[id^="${keys[0]}"];
For my hook friends:
Based on @ulitiy solution.import React, { useEffect } from 'react'; import { useFormikContext } from 'formik'; const FocusError = () => { const { errors, isSubmitting, isValidating } = useFormikContext(); useEffect(() => { if (isSubmitting && !isValidating) { let keys = Object.keys(errors); if (keys.length > 0) { const selector = `[name=${keys[0]}]`; const errorElement = document.querySelector(selector) as HTMLElement; if (errorElement) { errorElement.focus(); } } } }, [errors, isSubmitting, isValidating]); return null; }; export default FocusError;
Put it within formiks
Form
.<Formik ...> <Form> ... <FocusError /> </Form> </Formik>
Thanks to the guys above. 👍
Thanks!! its working fine..
(1/3)Snippet to getting first error(works even if nested case)
import { isObject } from "lodash";
export const getFirstErrorKey = (object: any, keys: string[] = []): any => {
let firstErrorKey = "";
if (Array.isArray(object)) {
for (let i = 0; i < object.length; i++) {
if (object[i]) {
firstErrorKey = Object.keys(object)[i];
break;
}
}
} else {
firstErrorKey = Object.keys(object)[0];
}
if (firstErrorKey && isObject(object[firstErrorKey])) {
return getFirstErrorKey(object[firstErrorKey], [...keys, firstErrorKey]);
}
return [...keys, firstErrorKey].join(".");
};
(2/3)Snippet to focus to that error input
import { getFirstErrorKey } from "./getFirstErrorKey";
export const focusElement = (errors: any) => {
let element = null;
const firstErrorKey = getFirstErrorKey(errors);
if (global.window.document.getElementsByName(firstErrorKey).length) {
element = global.window.document.getElementsByName(firstErrorKey)[0];
if (element instanceof HTMLInputElement) {
element.focus();
} else {
element = element.getElementsByTagName("input")[0];
if (element instanceof HTMLInputElement) {
element.focus();
}
}
}
};
(3/3)Final Usage
useEffect(() => {
if (!isValid && submitCount > 0) {
focusElement(errors);
}
}, [submitCount, isValid]);
Amazing guys! Thanks to all of you! :)
Quick note for those of you who use smooth scroll and are not getting the offset correctly,
I used this:
const pos = errorElement.style.position;
const top = errorElement.style.top;
errorElement.style.position = 'relative';
errorElement.style.top = '-100px';
errorElement.scrollIntoView({ behavior: 'smooth', block: 'start' });
errorElement.style.top = top;
errorElement.style.position = pos;
This is great :) (using the react hooks version), I don't think it works with react-select elements though
All good but not working with react-select
anybody able to fix for react-select?
I used this for ReactSelect :
import React, { useEffect } from 'react';
import { useFormikContext } from 'formik';
const ScrollToFieldError = ({ scrollBehavior = { behavior: 'smooth', block: 'center' } }) => {
const { submitCount, isValid, errors } = useFormikContext();
useEffect(() => {
if (isValid) return;
const fieldErrorNames = getFieldErrorNames(errors);
let element;
if (fieldErrorNames[0].includes('.type')) {
element = document.querySelector(`input[aria-label='${fieldErrorNames[0]}']`);
} else {
element = document.querySelector(`input[name='${fieldErrorNames[0]}']`);
}
if (!element) return;
// Scroll to first known error into view
element.scrollIntoView(scrollBehavior as any);
// Formik doesn't (yet) provide a callback for a client-failed submission,
// thus why this is implemented through a hook that listens to changes on
// the submit count.
}, [submitCount]);
return null;
};
Put ScrollToFieldError within formiks Form.
Pass aria-label props in ReactSelect .
For some reason I don't understand, when I run this logic, the nested object keys are not ordered.
Have you experienced this?