From 58756da9b15292af7d3f8af462fc5f88e500a8fe Mon Sep 17 00:00:00 2001 From: dyh_a Date: Tue, 19 Dec 2023 00:06:49 +0800 Subject: [PATCH] feat(shared/hooks): useStateRef --- .../src/hooks/__tests__/useStateRef.test.ts | 29 +++++++++++++++++++ packages/shared/src/hooks/index.ts | 1 + packages/shared/src/hooks/useStateRef.ts | 22 ++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 packages/shared/src/hooks/__tests__/useStateRef.test.ts create mode 100644 packages/shared/src/hooks/useStateRef.ts diff --git a/packages/shared/src/hooks/__tests__/useStateRef.test.ts b/packages/shared/src/hooks/__tests__/useStateRef.test.ts new file mode 100644 index 00000000..0834d2cd --- /dev/null +++ b/packages/shared/src/hooks/__tests__/useStateRef.test.ts @@ -0,0 +1,29 @@ +import { renderHook, act } from '@testing-library/react'; +import { useStateRef } from '@pkg/shared'; +import { useRef } from 'react'; + +describe('useStateRef', () => { + test('basic', () => { + let renderTimes = 0; + const hook = renderHook(() => { + renderTimes++; + const ref = useRef(1); + return [...useStateRef(ref.current), ref] as const; + }); + const [value, setValue, forceUpdate, ref] = hook.result.current; + + expect(renderTimes).toBe(1); + expect(value.current).toBe(1); + + act(() => setValue(2)); + expect(value.current).toBe(2); + expect(ref.current).toBe(1); + + act(() => { + ref.current = 3; + forceUpdate(); + }); + expect(ref.current).toBe(3); + expect(value.current).toBe(3); + }); +}); diff --git a/packages/shared/src/hooks/index.ts b/packages/shared/src/hooks/index.ts index add5edc9..0c2f19ac 100644 --- a/packages/shared/src/hooks/index.ts +++ b/packages/shared/src/hooks/index.ts @@ -17,3 +17,4 @@ export * from './useEventListenerOnMounted'; export * from './useBeforeDestroy'; export * from './useValueWithPrev'; export * from './useUniqueRoot'; +export * from './useStateRef'; diff --git a/packages/shared/src/hooks/useStateRef.ts b/packages/shared/src/hooks/useStateRef.ts new file mode 100644 index 00000000..3c913d13 --- /dev/null +++ b/packages/shared/src/hooks/useStateRef.ts @@ -0,0 +1,22 @@ +import { useForceUpdate } from './useForceUpdate'; +import { useCallback, useRef } from 'react'; +import { useWatch } from './useWatch'; + +export function useStateRef( + state: V, +): [ + stateRef: React.MutableRefObject, + setStateRef: (value: V) => void, + forceUpdate: () => void, +] { + const forceUpdate = useForceUpdate(); + const stateRef = useRef(state); + useWatch(state, (n) => (stateRef.current = n)); + + const setValue = useCallback((value: V) => { + stateRef.current = value; + forceUpdate(); + }, []); + + return [stateRef, setValue, forceUpdate]; +}