diff --git a/.changeset/fair-avocados-tap.md b/.changeset/fair-avocados-tap.md new file mode 100644 index 0000000000..f09fc840d5 --- /dev/null +++ b/.changeset/fair-avocados-tap.md @@ -0,0 +1,5 @@ +--- +"@clerk/clerk-js": patch +--- + +Bug fix: Always receive a new session verification object when UserVerification component mounts. diff --git a/packages/clerk-js/src/ui/components/UserVerification/index.tsx b/packages/clerk-js/src/ui/components/UserVerification/index.tsx index 955bc58ac3..6a5d1d3863 100644 --- a/packages/clerk-js/src/ui/components/UserVerification/index.tsx +++ b/packages/clerk-js/src/ui/components/UserVerification/index.tsx @@ -1,13 +1,20 @@ import type { __experimental_UserVerificationModalProps, __experimental_UserVerificationProps } from '@clerk/types'; -import React from 'react'; +import React, { useEffect } from 'react'; import { ComponentContext, withCoreSessionSwitchGuard } from '../../contexts'; import { Flow } from '../../customizables'; import { Route, Switch } from '../../router'; import { UserVerificationFactorOne } from './UserVerificationFactorOne'; import { UserVerificationFactorTwo } from './UserVerificationFactorTwo'; +import { useUserVerificationSession } from './useUserVerificationSession'; function UserVerificationRoutes(): JSX.Element { + const { invalidate } = useUserVerificationSession(); + useEffect(() => { + return () => { + invalidate(); + }; + }, []); return ( diff --git a/packages/clerk-js/src/ui/components/UserVerification/useUserVerificationSession.tsx b/packages/clerk-js/src/ui/components/UserVerification/useUserVerificationSession.tsx index 86c74c1cfb..9ac6740d84 100644 --- a/packages/clerk-js/src/ui/components/UserVerification/useUserVerificationSession.tsx +++ b/packages/clerk-js/src/ui/components/UserVerification/useUserVerificationSession.tsx @@ -1,23 +1,26 @@ import { useSession } from '@clerk/shared/react'; +import { useMemo } from 'react'; import { useUserVerification } from '../../contexts'; import { LoadingCard } from '../../elements'; import { useFetch } from '../../hooks'; -const useUserVerificationSession = () => { - const { session } = useSession(); +const useUserVerificationSessionKey = () => { const { level } = useUserVerification(); - const data = useFetch( - session ? session.__experimental_startVerification : undefined, - { + return useMemo( + () => ({ level: level || 'secondFactor', - // TODO(STEP-UP): Figure out if this needs to be a prop - maxAgeMinutes: 10, - }, - { - throttleTime: 300, - }, + }), + [level], ); +}; + +const useUserVerificationSession = () => { + const { session } = useSession(); + const key = useUserVerificationSessionKey(); + const data = useFetch(session ? session.__experimental_startVerification : undefined, key, { + throttleTime: 300, + }); return { ...data }; }; @@ -39,4 +42,4 @@ function withUserVerificationSessionGuard

(Component: React.ComponentType

): return Hoc; } -export { useUserVerificationSession, withUserVerificationSessionGuard }; +export { useUserVerificationSessionKey, useUserVerificationSession, withUserVerificationSessionGuard }; diff --git a/packages/clerk-js/src/ui/hooks/useFetch.ts b/packages/clerk-js/src/ui/hooks/useFetch.ts index ac3c7f63db..ba9337dadb 100644 --- a/packages/clerk-js/src/ui/hooks/useFetch.ts +++ b/packages/clerk-js/src/ui/hooks/useFetch.ts @@ -33,14 +33,16 @@ export const clearFetchCache = () => { const serialize = (key: unknown) => (typeof key === 'string' ? key : JSON.stringify(key)); -const useCache = ( +export const useCache = ( key: K, + serializer = serialize, ): { getCache: () => State | undefined; setCache: (state: State) => void; + clearCache: () => void; subscribeCache: (callback: () => void) => () => void; } => { - const serializedKey = serialize(key); + const serializedKey = serializer(key); const get = useCallback(() => requestCache.get(serializedKey), [serializedKey]); const set = useCallback( (data: State) => { @@ -49,6 +51,16 @@ const useCache = ( }, [serializedKey], ); + + const clear = useCallback(() => { + set({ + isLoading: false, + isValidating: false, + data: null, + error: null, + cachedAt: undefined, + }); + }, [set]); const subscribe = useCallback((callback: () => void) => { subscribers.add(callback); return () => subscribers.delete(callback); @@ -58,6 +70,7 @@ const useCache = ( getCache: get, setCache: set, subscribeCache: subscribe, + clearCache: clear, }; }; @@ -76,9 +89,9 @@ export const useFetch = ( staleTime?: number; }, ) => { - const { subscribeCache, getCache, setCache } = useCache(params); + const { subscribeCache, getCache, setCache, clearCache } = useCache(params); - const staleTime = options?.staleTime || 1000 * 60 * 2; //cache for 2 minutes by default + const staleTime = options?.staleTime ?? 1000 * 60 * 2; //cache for 2 minutes by default const throttleTime = options?.throttleTime || 0; const fetcherRef = useRef(fetcher); @@ -91,7 +104,7 @@ export const useFetch = ( useEffect(() => { const fetcherMissing = !fetcherRef.current; const isCacheStale = Date.now() - (getCache()?.cachedAt || 0) >= staleTime; - const isRequestOnGoing = getCache()?.isValidating; + const isRequestOnGoing = getCache()?.isValidating ?? false; if (fetcherMissing || !isCacheStale || isRequestOnGoing) { return; @@ -100,8 +113,8 @@ export const useFetch = ( const d = performance.now(); setCache({ - data: null, - isLoading: !getCache(), + data: getCache()?.data ?? null, + isLoading: !getCache()?.data, isValidating: true, error: null, }); @@ -126,7 +139,7 @@ export const useFetch = ( }) .catch(() => { setCache({ - data: null, + data: getCache()?.data ?? null, isLoading: false, isValidating: false, error: true, @@ -138,5 +151,6 @@ export const useFetch = ( return { ...cached, setCache, + invalidate: clearCache, }; };