import { useState, useEffect } from 'react';
function useYScroll(ref) {
const [yScroll, setYScroll] = useState(0);
useEffect(() => {
const handleScroll = () => {
const { scrollTop } = ref.current;
setYScroll(scrollTop);
};
ref.current.addEventListener('scroll', handleScroll);
return () => {
ref.current.removeEventListener('scroll', handleScroll);
};
}, [ref]);
return yScroll;
}
이 hook은 useRef
로 참조한 DOM 엘리먼트의 y-scroll
값을 추적합니다. useYScroll
을 사용하려면 해당 컴포넌트에서 useRef
를 사용하여 해당 DOM 엘리먼트에 대한 참조를 만들고 useYScroll
을 호출하여 y-scroll
값을 가져올 수 있습니다.
예를 들어, 다음과 같이 사용할 수 있습니다.
import { useRef } from 'react';
import useYScroll from './useYScroll';
function MyComponent() {
const ref = useRef(null);
const yScroll = useYScroll(ref);
return (
<div ref={ref} style={{ height: '200px', overflowY: 'scroll' }}>
<p>y-scroll value: {yScroll}</p>
</div>
);
}
다음은 React에서 특정 컴포넌트의 y-scroll
값을 0부터 1 사이의 값으로 반환해주는 custom hook
입니다.
import { useState, useEffect } from 'react';
function useYScroll(ref) {
const [yScroll, setYScroll] = useState(0);
useEffect(() => {
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = ref.current;
const scrollRatio = scrollTop / (scrollHeight - clientHeight);
setYScroll(scrollRatio);
};
ref.current.addEventListener('scroll', handleScroll);
return () => {
ref.current.removeEventListener('scroll', handleScroll);
};
}, [ref]);
return yScroll;
}
이 hook은 useRef
로 참조한 DOM 엘리먼트의 y-scroll
값을 추적합니다. useYScroll
을 사용하려면 해당 컴포넌트에서 useRef
를 사용하여 해당 DOM 엘리먼트에 대한 참조를 만들고 useYScroll
을 호출하여 0부터 1 사이의 y-scroll
값을 가져올 수 있습니다.
y-scroll
값을 업데이트합니다. 하지만 스크롤 이벤트는 매우 자주 발생하므로 불필요한 렌더링을 유발할 수 있습니다. 이를 개선하기 위해서는 스크롤 이벤트를 일정 주기로 쓰로틀링하거나 디바운싱해야 합니다.useEffect
에서 의존성 배열로 ref
를 사용합니다. 이는 매번 렌더링될 때마다 useEffect
가 호출되어 스크롤 이벤트를 추가하고 제거하므로 성능상 이슈가 될 수 있습니다. 이를 개선하기 위해서는 useMemo
를 사용하여 의존성 배열을 최적화할 수 있습니다.import { useState, useEffect, useMemo } from 'react';
function useYScroll(ref) {
const [yScroll, setYScroll] = useState(0);
const handleScroll = useMemo(() => {
return () => {
const { scrollTop, scrollHeight, clientHeight } = ref.current;
const scrollRatio = scrollTop / (scrollHeight - clientHeight);
setYScroll(scrollRatio);
};
}, [ref]);
useEffect(() => {
ref.current.addEventListener('scroll', handleScroll);
return () => {
ref.current.removeEventListener('scroll', handleScroll);
};
}, [ref, handleScroll]);
return yScroll;
}
여기서는 handleScroll 함수를 useMemo를 사용하여 캐싱합니다. 이제 의존성 배열을 최적화하면 handleScroll 함수가 변경될 때마다 useEffect가 호출되는 것을 방지할 수 있습니다.
쓰로틀링(throttling)이나 디바운싱(debouncing)을 적용하려면 Lodash와 같은 라이브러리에서 제공하는 함수를 사용하거나, 직접 함수를 작성할 수 있습니다. 여기서는 Lodash의 throttle 함수를 사용하여 useYScroll 훅을 개선하는 방법을 알아보겠습니다.
import { useState, useEffect, useMemo } from 'react';
import { throttle } from 'lodash';
function useYScroll(ref, wait = 100) {
const [yScroll, setYScroll] = useState(0);
const handleScroll = useMemo(() => {
return throttle(() => {
const { scrollTop, scrollHeight, clientHeight } = ref.current;
const scrollRatio = scrollTop / (scrollHeight - clientHeight);
setYScroll(scrollRatio);
}, wait);
}, [ref, wait]);
useEffect(() => {
ref.current.addEventListener('scroll', handleScroll);
return () => {
ref.current.removeEventListener('scroll', handleScroll);
handleScroll.cancel();
};
}, [ref, handleScroll]);
return yScroll;
}
여기서는 Lodash
에서 제공하는 throttle
함수를 사용하여 handleScroll
함수를 일정 시간(wait)마다 한 번씩 호출하도록 설정했습니다. handleScroll
함수가 매번 호출되지 않으므로 불필요한 렌더링을 방지할 수 있습니다.
handleScroll
함수를 이전에 사용한 useMemo
함수 안에 감싸서 함수가 매번 새로 생성되는 것을 방지하고 의존성 배열에 wait 값을 추가했습니다.
마지막으로, useEffect
함수에서 removeEventListener
를 호출할 때 handleScroll
함수를 cancel 해줌으로써 throttle
함수가 비활성화되도록 처리했습니다.
이제 useYScroll
훅은 스크롤 이벤트가 매우 자주 발생할 때도 성능적으로 안정적으로 동작할 수 있게 되었습니다.