From 2c30f6d6de05c2dc411ed70d068da2ed3e528609 Mon Sep 17 00:00:00 2001 From: Jonghyeon Ko Date: Fri, 22 Sep 2023 23:34:29 +0900 Subject: [PATCH] refactor(react): add useSetTimeout to reduce code (#158) * refactor(react): add useSetTimeout to reduce code * Create friendly-ants-grab.md --- .changeset/friendly-ants-grab.md | 5 +++++ packages/react/src/Delay.tsx | 10 +++------ packages/react/src/hooks/index.ts | 1 + .../react/src/hooks/useSetTimeout.spec.ts | 22 +++++++++++++++++++ packages/react/src/hooks/useSetTimeout.ts | 7 ++++++ packages/react/src/utils/toTest.tsx | 12 ++++------ 6 files changed, 42 insertions(+), 15 deletions(-) create mode 100644 .changeset/friendly-ants-grab.md create mode 100644 packages/react/src/hooks/useSetTimeout.spec.ts create mode 100644 packages/react/src/hooks/useSetTimeout.ts diff --git a/.changeset/friendly-ants-grab.md b/.changeset/friendly-ants-grab.md new file mode 100644 index 000000000..6e570e0be --- /dev/null +++ b/.changeset/friendly-ants-grab.md @@ -0,0 +1,5 @@ +--- +"@suspensive/react": patch +--- + +refactor(react): add useSetTimeout to reduce code diff --git a/packages/react/src/Delay.tsx b/packages/react/src/Delay.tsx index e4e3a0c93..3c12620ee 100644 --- a/packages/react/src/Delay.tsx +++ b/packages/react/src/Delay.tsx @@ -1,4 +1,5 @@ -import { ComponentProps, ComponentType, PropsWithChildren, createContext, useContext, useEffect, useState } from 'react' +import { ComponentProps, ComponentType, PropsWithChildren, createContext, useContext, useState } from 'react' +import { useSetTimeout } from './hooks' import { PropsWithoutChildren } from './types' type DelayProps = PropsWithChildren<{ @@ -8,12 +9,7 @@ export const Delay = ({ ms, children }: DelayProps) => { const delayContextValue = useContext(DelayContext) const delayMs = ms ?? delayContextValue.ms ?? 0 const [isDelayed, setIsDelayed] = useState(delayMs === 0) - - useEffect(() => { - const timerId = setTimeout(() => setIsDelayed(true), delayMs) - return () => clearTimeout(timerId) - }, [delayMs]) - + useSetTimeout(() => setIsDelayed(true), delayMs) return <>{isDelayed ? children : null} } diff --git a/packages/react/src/hooks/index.ts b/packages/react/src/hooks/index.ts index 41635fd29..7227cf4d9 100644 --- a/packages/react/src/hooks/index.ts +++ b/packages/react/src/hooks/index.ts @@ -2,3 +2,4 @@ export { useIsMounted } from './useIsMounted' export { useKey } from './useKey' export { usePrevious } from './usePrevious' export { useIsChanged } from './useIsChanged' +export { useSetTimeout } from './useSetTimeout' diff --git a/packages/react/src/hooks/useSetTimeout.spec.ts b/packages/react/src/hooks/useSetTimeout.spec.ts new file mode 100644 index 000000000..9f5467a82 --- /dev/null +++ b/packages/react/src/hooks/useSetTimeout.spec.ts @@ -0,0 +1,22 @@ +import { act, renderHook } from '@testing-library/react' +import { describe, expect, it, vi } from 'vitest' +import { MS_100 } from '../utils/toTest' +import { useSetTimeout } from '.' + +vi.useFakeTimers() + +describe('useSetTimeout', () => { + it('should run given function once after given timeout', () => { + const fn = vi.fn() + const rendered = renderHook(() => useSetTimeout(fn, MS_100)) + expect(fn).toHaveBeenCalledTimes(0) + act(() => vi.advanceTimersByTime(MS_100)) + expect(fn).toHaveBeenCalledTimes(1) + act(() => vi.advanceTimersByTime(MS_100)) + expect(fn).toHaveBeenCalledTimes(1) + rendered.rerender() + expect(fn).toHaveBeenCalledTimes(1) + act(() => vi.advanceTimersByTime(MS_100)) + expect(fn).toHaveBeenCalledTimes(1) + }) +}) diff --git a/packages/react/src/hooks/useSetTimeout.ts b/packages/react/src/hooks/useSetTimeout.ts new file mode 100644 index 000000000..5fed960d1 --- /dev/null +++ b/packages/react/src/hooks/useSetTimeout.ts @@ -0,0 +1,7 @@ +import { useEffect } from 'react' + +export const useSetTimeout = (fn: (...args: []) => void, delay: number) => + useEffect(() => { + const timeout = setTimeout(fn, delay) + return () => clearTimeout(timeout) + }, [fn, delay]) diff --git a/packages/react/src/utils/toTest.tsx b/packages/react/src/utils/toTest.tsx index 2309d7d7f..2043dc761 100644 --- a/packages/react/src/utils/toTest.tsx +++ b/packages/react/src/utils/toTest.tsx @@ -1,4 +1,5 @@ -import { PropsWithChildren, ReactNode, useEffect, useState } from 'react' +import { PropsWithChildren, ReactNode, useState } from 'react' +import { useSetTimeout } from '../hooks' const suspendIsNeed = { current: true } type SuspendProps = { during: number; toShow?: ReactNode } @@ -24,9 +25,7 @@ export const ThrowError = ({ message, after, children }: ThrowErrorProps) => { if (isNeedError) { throw new Error(message) } - useEffect(() => { - setTimeout(() => setIsNeedError(true), after) - }, [after]) + useSetTimeout(() => setIsNeedError(true), after) return <>{children} } @@ -36,10 +35,7 @@ export const ThrowNull = ({ after, children }: ThrowNullProps) => { if (isNeedError) { throw null } - useEffect(() => { - const timerId = setTimeout(() => setIsNeedError(true), after) - return () => clearTimeout(timerId) - }, [after]) + useSetTimeout(() => setIsNeedError(true), after) return <>{children} }