Created
June 2, 2021 22:38
-
-
Save drianoaz/be29bb6189e28ac341f9e3a840182a93 to your computer and use it in GitHub Desktop.
An optimized react hook for window resize
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 { renderHook } from '@testing-library/react-hooks'; | |
import { useWindowResize } from './useWindowResize'; | |
jest.useFakeTimers(); | |
describe('useWindowResize', () => { | |
it('should run the callback when the window is resized', () => { | |
const mock = jest.fn(); | |
renderHook(() => useWindowResize(mock)); | |
global.dispatchEvent(new Event('resize')); | |
jest.runAllTimers(); | |
expect(mock).toHaveBeenCalledTimes(1); | |
}); | |
describe('trottle', () => { | |
it('should run the callback', () => { | |
const mock = jest.fn(); | |
renderHook(() => useWindowResize(mock)); | |
global.dispatchEvent(new Event('resize')); | |
jest.advanceTimersByTime(50); | |
global.dispatchEvent(new Event('resize')); | |
jest.advanceTimersByTime(50); | |
global.dispatchEvent(new Event('resize')); | |
jest.advanceTimersByTime(50); | |
global.dispatchEvent(new Event('resize')); | |
jest.runAllTimers(); | |
expect(mock).toHaveBeenCalledTimes(1); | |
}); | |
it('should be able to resize many times', () => { | |
const mock = jest.fn(); | |
renderHook(() => useWindowResize(mock)); | |
global.dispatchEvent(new Event('resize')); | |
jest.runAllTimers(); | |
global.dispatchEvent(new Event('resize')); | |
jest.runAllTimers(); | |
expect(mock).toHaveBeenCalledTimes(2); | |
}); | |
}); | |
}); |
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 { useCallback, useEffect, useMemo, useRef } from 'react'; | |
const FPS_15 = 66; | |
/** | |
* run the callback for each window resize | |
* | |
* @see https://developer.mozilla.org/pt-BR/docs/Web/API/Window/resize_event | |
* @param callback the method that will be called; | |
*/ | |
export const useWindowResize = (callback: (event: Event) => void) => { | |
const requestRef = useRef<number | undefined>(undefined); | |
const memoizedCallback = useMemo(() => { | |
return callback; | |
/** | |
* As an expected behavior, the memoized callback must never be changed to | |
* prevent it being called twice immediately. Because of this, the callback | |
* can't be inside the useMemo dependencies and the lint rule must be disabled | |
*/ | |
}, []); // eslint-disable-line react-hooks/exhaustive-deps | |
const resizeTrottler = useCallback( | |
(event: UIEvent) => { | |
window.clearTimeout(requestRef.current); | |
requestRef.current = window.setTimeout(() => { | |
requestRef.current = undefined; | |
memoizedCallback(event); | |
}, FPS_15); | |
}, | |
[memoizedCallback], | |
); | |
useEffect(() => { | |
window.addEventListener('resize', resizeTrottler, false); | |
return () => { | |
window.removeEventListener('resize', resizeTrottler); | |
}; | |
}, [memoizedCallback, resizeTrottler]); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment