From 9797741ec9b02c5e0d7729037d94b9d2e71442cc Mon Sep 17 00:00:00 2001 From: lijianan <574980606@qq.com> Date: Sat, 18 Jul 2026 08:11:35 +0800 Subject: [PATCH 1/2] fix(useEvent): preserve handler context and improve typings --- src/hooks/useEvent.ts | 45 ++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/hooks/useEvent.ts b/src/hooks/useEvent.ts index d16360d1..6f927de6 100644 --- a/src/hooks/useEvent.ts +++ b/src/hooks/useEvent.ts @@ -1,21 +1,36 @@ -import * as React from 'react'; +import React from 'react'; +import { useState } from 'react'; -const useEvent = any) | undefined>( - callback: T, -): undefined extends T - ? ( - ...args: Parameters> - ) => ReturnType> | undefined - : T => { - const fnRef = React.useRef(callback); +type StableHandler = ( + this: This, + ...args: Args +) => Result; + +function useEvent( + fn: StableHandler, +): StableHandler; + +function useEvent< + This = unknown, + Args extends unknown[] = [], + Result = undefined, +>( + fn?: StableHandler, +): StableHandler; + +function useEvent( + callback?: StableHandler, +) { + const fnRef = React.useRef>(callback); fnRef.current = callback; - const memoFn = React.useCallback( - (...args: any[]) => fnRef.current?.(...args), - [], - ); + const [stableHandler] = useState(() => { + return function (this: This, ...args: Args) { + return fnRef.current?.apply(this, args); + }; + }); - return memoFn; -}; + return stableHandler; +} export default useEvent; From 0df47f2b3166cdfd61594af1ab3461f32520a8f8 Mon Sep 17 00:00:00 2001 From: lijianan <574980606@qq.com> Date: Sat, 18 Jul 2026 08:20:18 +0800 Subject: [PATCH 2/2] update --- src/hooks/useDelayState.ts | 10 +++++++--- src/hooks/useEvent.ts | 8 +++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/hooks/useDelayState.ts b/src/hooks/useDelayState.ts index 978f7421..643dd0cd 100644 --- a/src/hooks/useDelayState.ts +++ b/src/hooks/useDelayState.ts @@ -3,7 +3,8 @@ import raf from '../raf'; import useEvent from './useEvent'; export type DelayConfig = - { frame: number; ms?: never } | { frame?: never; ms: number }; + | { frame: number; ms?: never } + | { frame?: never; ms: number }; export type SetDelayState = ( nextValue: React.SetStateAction, @@ -37,8 +38,11 @@ export default function useDelayState( } }); - const setDelayValue = useEvent>( - (nextValue, immediatelyOrDelay) => { + const setDelayValue = useEvent( + ( + nextValue: React.SetStateAction, + immediatelyOrDelay?: boolean | DelayConfig, + ) => { const delayConfig = immediatelyOrDelay || { frame: 1 }; cancelPending(); diff --git a/src/hooks/useEvent.ts b/src/hooks/useEvent.ts index 6f927de6..49de15f1 100644 --- a/src/hooks/useEvent.ts +++ b/src/hooks/useEvent.ts @@ -1,6 +1,9 @@ import React from 'react'; import { useState } from 'react'; +const useIsomorphicLayoutEffect = + typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect; + type StableHandler = ( this: This, ...args: Args @@ -22,7 +25,10 @@ function useEvent( callback?: StableHandler, ) { const fnRef = React.useRef>(callback); - fnRef.current = callback; + + useIsomorphicLayoutEffect(() => { + fnRef.current = callback; + }, [callback]); const [stableHandler] = useState(() => { return function (this: This, ...args: Args) {