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
This commit is contained in:
simoleo89
2026-05-11 16:31:51 +00:00
parent 5697d169ee
commit 6c9f414028
+8 -4
View File
@@ -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<TurnstileWidgetProps> = props =>
const widgetIdRef = useRef<string | null>(null);
const [ scriptReady, setScriptReady ] = useState<boolean>(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<TurnstileWidgetProps> = 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 () =>