From 6a46425515c3f4ca795bcdd42059b5f932346118 Mon Sep 17 00:00:00 2001 From: Alex Carpenter Date: Mon, 22 Jul 2024 15:57:52 -0400 Subject: [PATCH] fix(elements): only validate `onFocus` or `onBlur` when value has changed (#3778) --- .changeset/strong-beers-compete.md | 5 +++++ .../elements/src/react/common/form/index.tsx | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 .changeset/strong-beers-compete.md diff --git a/.changeset/strong-beers-compete.md b/.changeset/strong-beers-compete.md new file mode 100644 index 0000000000..c76391add4 --- /dev/null +++ b/.changeset/strong-beers-compete.md @@ -0,0 +1,5 @@ +--- +"@clerk/elements": patch +--- + +Fix issue where password field validation was incorrectly showing successful field state due to input being refocused on invalid form submission diff --git a/packages/elements/src/react/common/form/index.tsx b/packages/elements/src/react/common/form/index.tsx index dbd6553bdb..5cbfa021b0 100644 --- a/packages/elements/src/react/common/form/index.tsx +++ b/packages/elements/src/react/common/form/index.tsx @@ -94,6 +94,16 @@ const enrichFieldState = (validity: ValidityState | undefined, fieldState: Field * Hooks * -----------------------------------------------------------------------------------------------*/ +function usePrevious(value: T): T | undefined { + const ref = React.useRef(); + + React.useEffect(() => { + ref.current = value; + }, [value]); + + return ref.current; +} + const useGlobalErrors = () => { const errors = useFormSelector(globalErrorsSelector); @@ -251,6 +261,7 @@ const useInput = ({ }, }); const value = useFormSelector(fieldValueSelector(name)); + const prevValue = usePrevious(value); const hasValue = Boolean(value); const type = inputType ?? determineInputTypeFromName(rawName); let shouldValidatePassword = false; @@ -288,21 +299,21 @@ const useInput = ({ const onBlur = React.useCallback( (event: React.FocusEvent) => { onBlurProp?.(event); - if (shouldValidatePassword) { + if (shouldValidatePassword && event.target.value !== prevValue) { validatePassword(event.target.value); } }, - [onBlurProp, shouldValidatePassword, validatePassword], + [onBlurProp, shouldValidatePassword, validatePassword, prevValue], ); const onFocus = React.useCallback( (event: React.FocusEvent) => { onFocusProp?.(event); - if (shouldValidatePassword) { + if (shouldValidatePassword && event.target.value !== prevValue) { validatePassword(event.target.value); } }, - [onFocusProp, shouldValidatePassword, validatePassword], + [onFocusProp, shouldValidatePassword, validatePassword, prevValue], ); React.useEffect(() => {