From 60a49372e7553c00dcb36387870d35f5380afdb4 Mon Sep 17 00:00:00 2001 From: Ward Date: Mon, 2 Sep 2019 22:06:53 +1000 Subject: [PATCH] feat: improve useWindowSize performance rAF --- src/__tests__/useWindowSize.test.tsx | 29 ++++++++++++++++++++++++---- src/useWindowSize.ts | 21 +++++++++++++++----- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/__tests__/useWindowSize.test.tsx b/src/__tests__/useWindowSize.test.tsx index b3cb7fe5e4..015cf495c2 100644 --- a/src/__tests__/useWindowSize.test.tsx +++ b/src/__tests__/useWindowSize.test.tsx @@ -1,6 +1,24 @@ import { act, renderHook } from '@testing-library/react-hooks'; +import { replaceRaf } from 'raf-stub'; import useWindowSize from '../useWindowSize'; +interface RequestAnimationFrame { + reset(): void; + step(): void; +} + +declare var requestAnimationFrame: RequestAnimationFrame; + +replaceRaf(); + +beforeEach(() => { + requestAnimationFrame.reset(); +}); + +afterEach(() => { + requestAnimationFrame.reset(); +}); + // simulate window resize function fireResize(type, value) { switch (type) { @@ -27,12 +45,14 @@ describe('useWindowSize', () => { it('should update width', () => { act(() => { fireResize('width', 320); - hook.rerender(); + requestAnimationFrame.step(); }); + expect(hook.result.current.width).toBe(320); + act(() => { fireResize('width', 640); - hook.rerender(); + requestAnimationFrame.step(); }); expect(hook.result.current.width).toBe(640); }); @@ -40,12 +60,13 @@ describe('useWindowSize', () => { it('should update height', () => { act(() => { fireResize('height', 500); - hook.rerender(); + requestAnimationFrame.step(); }); expect(hook.result.current.height).toBe(500); + act(() => { fireResize('height', 1000); - hook.rerender(); + requestAnimationFrame.step(); }); expect(hook.result.current.height).toBe(1000); }); diff --git a/src/useWindowSize.ts b/src/useWindowSize.ts index fc5ec2f82d..3f081a6a79 100644 --- a/src/useWindowSize.ts +++ b/src/useWindowSize.ts @@ -1,7 +1,8 @@ -import { useEffect, useState } from 'react'; +import { useRef, useEffect, useState } from 'react'; import { isClient } from './util'; const useWindowSize = (initialWidth = Infinity, initialHeight = Infinity) => { + const frame = useRef(0); const [state, setState] = useState<{ width: number; height: number }>({ width: isClient ? window.innerWidth : initialWidth, height: isClient ? window.innerHeight : initialHeight, @@ -10,13 +11,23 @@ const useWindowSize = (initialWidth = Infinity, initialHeight = Infinity) => { useEffect(() => { if (isClient) { const handler = () => { - setState({ - width: window.innerWidth, - height: window.innerHeight, + cancelAnimationFrame(frame.current); + + frame.current = requestAnimationFrame(() => { + setState({ + width: window.innerWidth, + height: window.innerHeight, + }); }); }; + window.addEventListener('resize', handler); - return () => window.removeEventListener('resize', handler); + + return () => { + cancelAnimationFrame(frame.current); + + window.removeEventListener('resize', handler); + }; } else { return undefined; }