Created
January 25, 2023 13:12
-
-
Save nilscox/a0fa520f72435278700b9fcfbd27a603 to your computer and use it in GitHub Desktop.
React hook to wait for some check to be true
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { act, renderHook } from '@testing-library/react' | |
import { useWaitFor } from './use-wait-for' | |
describe('useWaitFor', () => { | |
beforeEach(() => { | |
jest.useFakeTimers() | |
}) | |
it('calls then when the condition is true', () => { | |
let check = jest.fn().mockReturnValue(false) | |
const onTimeout = jest.fn() | |
const then = jest.fn() | |
const { result, rerender } = renderHook(() => useWaitFor({ check, then, timeout: 1, onTimeout }, [check])) | |
act(result.current[0]) | |
expect(then).not.toHaveBeenCalled() | |
check = jest.fn().mockReturnValue(true) | |
rerender({ check, then }) | |
act(() => void jest.runAllTimers()) | |
expect(then).toHaveBeenCalled() | |
expect(onTimeout).not.toHaveBeenCalled() | |
}) | |
it('returns the pending flag to true when the timeout is running', () => { | |
const check = jest.fn().mockReturnValue(false) | |
const onTimeout = jest.fn() | |
const then = jest.fn() | |
const { result, rerender } = renderHook(() => useWaitFor({ check, then, timeout: 1, onTimeout }, [{}])) | |
expect(result.current[1]).toBe(false) | |
act(result.current[0]) | |
expect(result.current[1]).toBe(true) | |
check.mockReturnValue(true) | |
rerender({ check }) | |
act(() => void jest.runAllTimers()) | |
expect(result.current[1]).toBe(false) | |
act(result.current[0]) | |
act(() => void jest.runAllTimers()) | |
expect(result.current[1]).toBe(false) | |
}) | |
it('does not set the timeout when the condition is already true', () => { | |
const check = jest.fn().mockReturnValue(true) | |
const onTimeout = jest.fn() | |
const then = jest.fn() | |
const { result } = renderHook(() => useWaitFor({ check, then, timeout: 1, onTimeout }, [{}])) | |
const [execute] = result.current | |
expect(result.current[1]).toBe(false) | |
act(execute) | |
expect(result.current[1]).toBe(false) | |
expect(then).toHaveBeenCalled() | |
}) | |
it('calls onTimeout when the condition remains false after a timeout', () => { | |
const check = jest.fn().mockReturnValue(false) | |
const onTimeout = jest.fn() | |
const then = jest.fn() | |
const { result } = renderHook(() => useWaitFor({ check, then, timeout: 1, onTimeout }, [check])) | |
act(() => { | |
result.current[0]() | |
jest.runAllTimers() | |
}) | |
expect(onTimeout).toHaveBeenCalled() | |
expect(then).not.toHaveBeenCalled() | |
}) | |
it('does not call onTimeout when the condition is true', () => { | |
const check = jest.fn().mockReturnValue(true) | |
const onTimeout = jest.fn() | |
const then = jest.fn() | |
const { result, rerender } = renderHook(() => useWaitFor({ check, then, timeout: 1, onTimeout }, [{}])) | |
act(result.current[0]) | |
rerender() | |
act(() => void jest.runAllTimers()) | |
expect(then).toHaveBeenCalled() | |
expect(onTimeout).not.toHaveBeenCalled() | |
}) | |
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DependencyList, useEffect, useState } from 'react' | |
type WaitForOptions = { | |
check: () => boolean | |
then: () => void | |
timeout: number | |
onTimeout: () => void | |
} | |
type WaitForResult = [() => void, boolean] | |
export const useWaitFor = ( | |
{ check, then, timeout, onTimeout }: WaitForOptions, | |
deps: DependencyList | |
): WaitForResult => { | |
const [timeoutId, setTimeoutId] = useState<number>() | |
useEffect(() => { | |
if (timeoutId === undefined) { | |
return | |
} | |
if (check()) { | |
then() | |
window.clearTimeout(timeoutId) | |
setTimeoutId(undefined) | |
} | |
// eslint-disable-next-line react-hooks/exhaustive-deps | |
}, deps) | |
const onStart = () => { | |
if (check()) { | |
then() | |
} else { | |
setTimeoutId( | |
window.setTimeout(() => { | |
onTimeout() | |
setTimeoutId(undefined) | |
}, timeout) | |
) | |
} | |
} | |
return [onStart, timeoutId !== undefined] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment