문법


특정 요소가 위치한 곳까지 스크롤을 이동하고 싶을 때 element.scrollIntoView 메서드를 이용하면 간편하게 구현할 수 있다. scrollIntoView 메서드는 총 3가지 방법으로 사용할 수 있다. (MDN)

element.scrollIntoView(align)

  1. 파라미터 없음 — element가 브라우저 화면 가장 위로 오도록 스크롤(정렬)

    element.scrollIntoView() // element.scrollIntoView(true)와 동일
    
  2. boolean 파라미터

    element.scrollIntoView(true) // { block: "start", inline: "nearest" } 옵션과 동일
    element.scrollIntoView(false) // { block: "end", inline: "nearest" } 옵션과 동일
    
  3. 옵션 객체 파라미터

    element.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
    

    <aside> <img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" /> block, inline 옵션값 설명

함수 응용 예시


const moveToTop = () => document.body.scrollIntoView(true); // 상단으로 이동
const moveToBottom = () => document.body.scrollIntoView(false); // 하단으로 이동

커스텀 훅 사용 예시


// 컴포넌트 본문 (배열 orders, Set 객체 selectedOrderIds를 prop으로 받음)
const tableRowRefs = useRef(
  orders.map(({ order_id }) => ({
    id: order_id,
    element: createRef<HTMLTableRowElement>(), // ref 객체 여러개 생성
  }))
);

useScrollIntoView({
  isActive: selectedOrderIds.size === 1, // true 일때만 활성화
  ref: tableRowRefs.current.find(({ id }) => id === [...selectedOrderIds][0])?.element,
  align: { behavior: "smooth", block: "center" }, // align 옵션
});

return (
	<div>
    {orders.map((order, i, { length }) => (
      <div ref={tableRowRefs.current[i].element}>{/* ... */}</div>
    ))}
  </div>
);
interface Props {
  isActive: boolean; // useScrollIntoView 훅 활성화 여부
  ref: RefObject<HTMLElement> | undefined;
  align?: ScrollIntoViewOptions; // scrollIntoView 메서드 align 옵션
}

/** 첫 렌더링에만 선택한 엘리먼트가 위치한 곳으로 스크롤하는 Hook */
export default function useScrollIntoView({ isActive, ref, align }: Props) {
  const isNavigated = useRef(false); // 요소가 있는곳까지 스크롤 했는지 여부

	useEffect(() => {
	  if (isActive && !isNavigated.current) {
	    ref?.current?.scrollIntoView(align);
	    isNavigated.current = true; // 첫 렌더링에만 스크롤하기 위해 isNavigated 값 true로 변경
	  }
	}, [align, isActive, ref]);
}