From 0145e214efd4c9a924efe360730f5c324c8196f3 Mon Sep 17 00:00:00 2001 From: Clement Date: Sat, 19 Apr 2025 15:23:44 +0700 Subject: [PATCH 1/5] feat: add useStableCallback docs --- .../tiny-react-hooks/src/useStableCallback/useStableCallback.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/tiny-react-hooks/src/useStableCallback/useStableCallback.md diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.md b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.md new file mode 100644 index 0000000..fb35636 --- /dev/null +++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.md @@ -0,0 +1 @@ +A hook that keep the callback always stable and do not need to add to dependencies of other hooks \ No newline at end of file From 5f8133bc04fd196275dd364c57893cdbd96ca709 Mon Sep 17 00:00:00 2001 From: Clement Date: Sat, 19 Apr 2025 15:24:03 +0700 Subject: [PATCH 2/5] feat: implement useStableCallback --- .../useStableCallback/useStableCallback.ts | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 packages/tiny-react-hooks/src/useStableCallback/useStableCallback.ts diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.ts b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.ts new file mode 100644 index 0000000..317c3ab --- /dev/null +++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.ts @@ -0,0 +1,30 @@ +import { useEffect, useMemo, useRef } from 'react'; + +/** Hook return type */ +type UseStableCallbackReturnType unknown> = T; + +/** + * Custom hook that ... + * @param {Function} [fn] - your function need to be stable + * @returns {UseStableCallbackReturnType} return the the stable callback that will apply the newest function + * @public + * @example + * ```tsx + * const stableFn = useStableCallback((a: number, b: number) => { + * return a + b; + * }); + * + * console.log(stableFn(1, 2)); // 3 + * ``` + */ +export function useStableCallback unknown>( + fn: T, +): UseStableCallbackReturnType { + const callbackRef = useRef(fn); + + useEffect(() => { + callbackRef.current = fn; + }, [fn]); + + return useMemo(() => ((...args) => callbackRef.current(...args)) as T, []); +} From e80593094d72fdc96be159d68facc52ef2301551 Mon Sep 17 00:00:00 2001 From: Clement Date: Sat, 19 Apr 2025 15:24:22 +0700 Subject: [PATCH 3/5] test: add tests for useStableCallback --- .../useStableCallback.test.ts | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/tiny-react-hooks/src/useStableCallback/useStableCallback.test.ts diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.test.ts b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.test.ts new file mode 100644 index 0000000..15705d0 --- /dev/null +++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.test.ts @@ -0,0 +1,27 @@ +import { renderHook } from '@testing-library/react'; + +import { useStableCallback } from './useStableCallback'; + +describe('useStableCallback()', () => { + it('should return the callback', () => { + const mockFn = vi.fn(() => 1); + const { result } = renderHook(() => useStableCallback(mockFn)); + + expect(result.current()).toBe(1); + expect(typeof result.current).toBe('function'); + }); + + describe('when fn is changed', () => { + it('should return the correct callback', () => { + const mockFn1 = vi.fn(() => 1); + const mockFn2 = vi.fn(() => 2); + const { result, rerender } = renderHook( + ({ fn }) => useStableCallback(fn), + { initialProps: { fn: mockFn1 } }, + ); + expect(result.current()).toBe(1); + rerender({ fn: mockFn2 }); + expect(result.current()).toBe(2); + }); + }); +}); From 2b104a9270291f02ef3a418400db6b34009c6c7a Mon Sep 17 00:00:00 2001 From: Clement Date: Sat, 19 Apr 2025 15:24:49 +0700 Subject: [PATCH 4/5] feat: export hook --- packages/tiny-react-hooks/src/index.ts | 1 + packages/tiny-react-hooks/src/useStableCallback/index.ts | 1 + 2 files changed, 2 insertions(+) create mode 100644 packages/tiny-react-hooks/src/useStableCallback/index.ts diff --git a/packages/tiny-react-hooks/src/index.ts b/packages/tiny-react-hooks/src/index.ts index d2b6108..d43351e 100644 --- a/packages/tiny-react-hooks/src/index.ts +++ b/packages/tiny-react-hooks/src/index.ts @@ -1 +1,2 @@ export * from './useDisclosure'; +export * from './useStableCallback'; diff --git a/packages/tiny-react-hooks/src/useStableCallback/index.ts b/packages/tiny-react-hooks/src/useStableCallback/index.ts new file mode 100644 index 0000000..9e86eaa --- /dev/null +++ b/packages/tiny-react-hooks/src/useStableCallback/index.ts @@ -0,0 +1 @@ +export * from './useStableCallback'; From f712bece0e71e7b9d8d26c0c0e2918a3c3a8029f Mon Sep 17 00:00:00 2001 From: Clement Date: Sat, 19 Apr 2025 15:25:11 +0700 Subject: [PATCH 5/5] docs: add example for useStableCallback --- .../useStableCallback/useStableCallback.demo.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 packages/tiny-react-hooks/src/useStableCallback/useStableCallback.demo.tsx diff --git a/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.demo.tsx b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.demo.tsx new file mode 100644 index 0000000..beef0be --- /dev/null +++ b/packages/tiny-react-hooks/src/useStableCallback/useStableCallback.demo.tsx @@ -0,0 +1,15 @@ +import { useStableCallback } from './useStableCallback'; + +interface Props { + count: number; +} + +export default function Component({ + count, +}: Props) { + const sum = useStableCallback(() => { + console.log(count * 10); + }); + + return ; +}