From 500a882a21636796bcafd2428f39c76642b10119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?E=CC=81ric=20D?= <15271+edas@users.noreply.github.com> Date: Wed, 29 Jan 2020 16:24:14 +0100 Subject: [PATCH] feat: Adds useInterval see jsdoc --- react/hooks/usePeriodicRender.js | 23 +++++++++ react/hooks/usePeriodicRender.spec.js | 69 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 react/hooks/usePeriodicRender.js create mode 100644 react/hooks/usePeriodicRender.spec.js diff --git a/react/hooks/usePeriodicRender.js b/react/hooks/usePeriodicRender.js new file mode 100644 index 0000000000..6ed91600d4 --- /dev/null +++ b/react/hooks/usePeriodicRender.js @@ -0,0 +1,23 @@ +import { useEffect, useState } from 'react' + +/** + * Force a new render each `duration` ms + * + * @param {number} duration - in millisecond + * @returns {Date} last trigger date for a rerender + */ +export default function usePeriodicRender(duration) { + const [lastExecuted, setLastExecuted] = useState(undefined) + useEffect(() => { + if (duration) { + const interval = window.setInterval(() => { + setLastExecuted(new Date()) + }, duration) + return () => { + window.clearInterval(interval) + } + } + }, [duration]) + + return lastExecuted +} diff --git a/react/hooks/usePeriodicRender.spec.js b/react/hooks/usePeriodicRender.spec.js new file mode 100644 index 0000000000..be82c1e583 --- /dev/null +++ b/react/hooks/usePeriodicRender.spec.js @@ -0,0 +1,69 @@ +import usePeriodicRender from './usePeriodicRender' +import { renderHook } from '@testing-library/react-hooks' + +async function wait(duration) { + return new Promise(resolve => { + window.setTimeout(() => { + resolve() + }, duration) + }) +} + +describe('usePeriodicRender', () => { + describe('with an interval as argument', () => { + it('should trigger a rerender after some time', async () => { + const before = Date.now() + const { waitForNextUpdate } = renderHook(() => usePeriodicRender(100)) + await waitForNextUpdate() + const after = Date.now() + expect(after - before).toBeGreaterThan(95) + expect(after - before).toBeLessThan(250) + }) + + it('should trigger a rerender again after waiting even more', async () => { + const { waitForNextUpdate } = renderHook(() => usePeriodicRender(100)) + await waitForNextUpdate() + const before = Date.now() + await waitForNextUpdate() + const after = Date.now() + expect(after - before).toBeGreaterThan(95) + expect(after - before).toBeLessThan(250) + }) + + it('should return a last execution date after triggering a rerender', async () => { + const before = Date.now() + const { result, waitForNextUpdate } = renderHook(() => + usePeriodicRender(100) + ) + await waitForNextUpdate() + expect(result.current).toBeInstanceOf(Date) + const after = result.current.getTime() + expect(after - before).toBeGreaterThan(95) + expect(after - before).toBeLessThan(250) + }) + + describe('when changing the duration', () => { + it('should change the interval of rerender', async () => { + const { waitForNextUpdate, rerender } = renderHook( + duration => usePeriodicRender(duration), + { initialProps: 20 } + ) + await waitForNextUpdate() + const before = Date.now() + await rerender(250) + await waitForNextUpdate() + const after = Date.now() + expect(after - before).toBeGreaterThan(245) + }) + }) + }) + + describe('with a falsy value as argument', () => { + it('should not trigger a rerender', async () => { + const { result } = renderHook(() => usePeriodicRender(false)) + const prev = result.current + wait(500) + expect(result.current).toEqual(prev) + }) + }) +})