diff --git a/src/misc/util.ts b/src/misc/util.ts index f47f4331..34fa42c2 100644 --- a/src/misc/util.ts +++ b/src/misc/util.ts @@ -19,4 +19,5 @@ export function off( } export const isBrowser = typeof window !== 'undefined'; + export const isNavigator = typeof navigator !== 'undefined'; diff --git a/src/useUpdate.ts b/src/useUpdate.ts index bcc4511a..26467396 100644 --- a/src/useUpdate.ts +++ b/src/useUpdate.ts @@ -2,9 +2,8 @@ import { useReducer } from 'react'; const updateReducer = (num: number): number => (num + 1) % 1_000_000; -const useUpdate = () => { +export default function useUpdate(): () => void { const [, update] = useReducer(updateReducer, 0); - return update as () => void; -}; -export default useUpdate; + return update; +} diff --git a/tests/useUpdate.test.ts b/tests/useUpdate.test.ts index aecf39be..6dd8752c 100644 --- a/tests/useUpdate.test.ts +++ b/tests/useUpdate.test.ts @@ -1,35 +1,70 @@ import { act, renderHook } from '@testing-library/react-hooks'; import useUpdate from '../src/useUpdate'; -it('should init update function', () => { - const { result } = renderHook(() => useUpdate()); - const update = result.current; - - expect(update).toBeInstanceOf(Function); -}); - -it('should forces a re-render every time update function is called', () => { - let renderCount = 0; - const { result } = renderHook(() => { - renderCount++; - return useUpdate(); +describe('useUpdate', () => { + it('should be defined', () => { + expect(useUpdate).toBeDefined(); }); - const update = result.current; - expect(renderCount).toBe(1); + it('should return a function', () => { + const { result } = renderHook(() => useUpdate()); - act(() => update()); - expect(renderCount).toBe(2); + expect(typeof result.current).toBe('function'); + }) - act(() => update()); - expect(renderCount).toBe(3); -}); - -it('should return same update function instance on each update', () => { - const { result, rerender } = renderHook(() => useUpdate()); - const { current: updateCb } = result; - - rerender(); - - expect(Object.is(result.current, updateCb)).toBe(true); + it('should re-render component each time returned function is called', () => { + let renders = 0; + const { result: { current: update } } = renderHook(() => { + renders++; + return useUpdate(); + }); + + expect(renders).toBe(1); + + act(() => update()); + expect(renders).toBe(2); + + act(() => update()); + expect(renders).toBe(3); + }) + + it('should return exact same function in between renders', () => { + let renders = 0; + const { result } = renderHook(() => { + renders++; + return useUpdate(); + }); + let initialUpdateFn = result.current; + + expect(renders).toBe(1); + + act(() => result.current()); + expect(renders).toBe(2); + expect(initialUpdateFn).toBe(result.current); + + act(() => result.current()); + expect(renders).toBe(3); + expect(initialUpdateFn).toBe(result.current); + }) + + it('passing the argument to returned function should not affect the use', () => { + let renders = 0; + const { result } = renderHook(() => { + renders++; + return useUpdate(); + }); + let initialUpdateFn = result.current; + + expect(renders).toBe(1); + + /* @ts-expect-error */ + act(() => result.current(1)); + expect(renders).toBe(2); + expect(initialUpdateFn).toBe(result.current); + + /* @ts-expect-error */ + act(() => result.current(1)); + expect(renders).toBe(3); + expect(initialUpdateFn).toBe(result.current); + }) });