Skip to content

Commit

Permalink
Merge pull request #99 from JohnsonMao/feature/use-request-animation-…
Browse files Browse the repository at this point in the history
…frame-state

- ✨ add request animation frame state hook
- ⚡️ optimization scroll event with RAF
  • Loading branch information
JohnsonMao authored Oct 1, 2023
2 parents 7d7c35c + e39be52 commit 20cf2f3
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 3 deletions.
11 changes: 11 additions & 0 deletions src/hooks/__tests__/useScroll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import { act, renderHook } from '@testing-library/react';
import useScroll from '../useScroll';

describe('useScroll hook', () => {
beforeAll(() => {
const mockRaf = jest.fn();

mockRaf.mockImplementation((fn: () => void) => {
fn();
return 1;
})

window.requestAnimationFrame = mockRaf;
})

it('should monitor and update scroll position for the window', async () => {
const { result } = renderHook(() => useScroll());

Expand Down
25 changes: 25 additions & 0 deletions src/hooks/useRafState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useCallback, useEffect, useRef, useState } from 'react';

/**
* This hook for managing state with RAF (requestAnimationFrame) optimization.
*/
const useRafState = <S>(initialState: S | (() => S)) => {
const frame = useRef(0);
const [state, setState] = useState(initialState);

const setRafState = useCallback((value: S | ((prevState: S) => S)) => {
cancelAnimationFrame(frame.current);

frame.current = requestAnimationFrame(() => {
setState(value);
});
}, []);

useEffect(() => {
return () => cancelAnimationFrame(frame.current);
}, [])

return [state, setRafState] as const;
};

export default useRafState;
7 changes: 4 additions & 3 deletions src/hooks/useScroll.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
import { RefObject, useCallback, useEffect, useRef } from 'react';
import useRafState from './useRafState';

type ScrollElement = HTMLElement | Window | null;

/**
* This hook allows tracking the scroll position of a specified DOM element or the window.
*/
function useScroll(ref?: RefObject<ScrollElement>) {
const [scroll, setScroll] = useState({ x: 0, y: 0 });
const [scroll, setScroll] = useRafState({ x: 0, y: 0 });
const internalElementRef = useRef<ScrollElement>(null);

const handleScroll = useCallback(() => {
Expand All @@ -17,7 +18,7 @@ function useScroll(ref?: RefObject<ScrollElement>) {
} else if (element instanceof Element) {
setScroll({ x: element.scrollLeft, y: element.scrollTop });
}
}, []);
}, [setScroll]);

const setInternalElementRef = useCallback(
(element: ScrollElement) => {
Expand Down

0 comments on commit 20cf2f3

Please sign in to comment.