From 6c9f414028c9679096d24d3bc17ec8c4e33ec34c Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Mon, 11 May 2026 16:31:51 +0000 Subject: [PATCH] Apply useEffectEvent (React 19.2) to TurnstileWidget callbacks The Turnstile render effect had a stale-closure hazard: it captured onToken/onExpire/onError props but didn't list them in the dependency array (deps: scriptReady, siteKey, theme, size). On parent re-renders the captured callbacks could go stale. Wrap the three callback props with useEffectEvent so they always read the latest props without invalidating the render effect. The render effect still only re-runs when the script readiness or widget config truly change. useEffectEvent shipped in React 19.2 (already on the project) and @types/react 19.2.x exports it. https://claude.ai/code/session_01GrR87LAqnAEyKG2ZbmQt5Q --- src/components/login/TurnstileWidget.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/components/login/TurnstileWidget.tsx b/src/components/login/TurnstileWidget.tsx index 2bd1496..8754231 100644 --- a/src/components/login/TurnstileWidget.tsx +++ b/src/components/login/TurnstileWidget.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useRef, useState } from 'react'; +import { FC, useEffect, useEffectEvent, useRef, useState } from 'react'; declare global { @@ -33,6 +33,10 @@ export const TurnstileWidget: FC = props => const widgetIdRef = useRef(null); const [ scriptReady, setScriptReady ] = useState(typeof window !== 'undefined' && !!window.turnstile); + const handleToken = useEffectEvent((token: string) => onToken(token)); + const handleExpire = useEffectEvent(() => onExpire?.()); + const handleError = useEffectEvent(() => onError?.()); + useEffect(() => { if(scriptReady) return; @@ -58,9 +62,9 @@ export const TurnstileWidget: FC = props => sitekey: siteKey, theme, size, - callback: (token: string) => onToken(token), - 'expired-callback': () => onExpire?.(), - 'error-callback': () => onError?.() + callback: handleToken, + 'expired-callback': handleExpire, + 'error-callback': handleError }); return () =>