[React] useRef를 통해 특정 요소로 스크롤 이동시키기(ft. Typescript 약간)

박기영·2022년 8월 12일
4

React

목록 보기
8/32

문제 상황

어떤 목록이 주르륵 내려와 있고, 그 중 어떤 것을 클릭하면 해당 목록에 해당하는 내용으로 주르륵 이동하는 기능을 가진 것들이 있다.
Velog에서도 볼 수 있듯이 오른쪽에 고정된 리스트들이 있고, 그 중 어떤 것을 클릭하면 그 내용으로 이동한다.
이는 어떻게 구현할 수 있을까?

필요한 것들

  1. useRef를 통해 특정 DOM 요소 참조하기
  2. 클릭 시 이동하기 위한 목록 만들기
  3. 목록 클릭 시 실행될 스크롤 이동 함수 만들기

이 3가지 정도면 쉽게 구현할 수 있다.
필자의 코드를 예시로 들어보겠다!

1. useRef로 DOM 요소 참조하기

// ArtistPage.tsx
// import 생략 //

function ArtistPage() {
  const artistRef = useRef<HTMLDivElement>(null);
  const two = useRef<HTMLDivElement>(null);
  const three = useRef<HTMLDivElement>(null);
  const four = useRef<HTMLDivElement>(null);
  const five = useRef<HTMLDivElement>(null);

  const refArr = [artistRef, two, three, four, five];
  
  return (
    <Layout>
	  // ... //
      <article className="px-3 flex flex-col xl:px-[50px]">
        {artistArr.map((item, index) => (
          <div
            className="mb-8 flex flex-col items-center lg:odd:items-end lg:even:items-start"
            key={index}
            ref={refArr[index]}
          >
			// ... //
          </div>
        ))}
      </article>
    </Layout>
  );
}

export default ArtistPage;

필요한 개수만큼 useRef를 선언하고 그 것들을 하나의 배열에 넣어놨다.
map() 메서드의 index를 사용해서 각각의 ref를 각각의 div에 달아주었다.
1번 끝! 각각의 요소에 ref를 달아주었다.
참고로, 이 컴포넌트는 세로로 길게 이미지들이 나열되어 있는 페이지이다.

2. 클릭 시 이동하기 위한 목록 만들기

// ArtistList.tsx
// import 생략 //

function ArtistsList() {
  return (
    <nav className="hidden lg:flex justify-center w-screen top-1/2 fixed">
      <ul className="flex flex-col items-center font-semibold xl:text-xl">
        {artistArr.map((item, index) => (
          <li
            className="hover:cursor-pointer"
            key={index}
          >
            {item.name}
          </li>
        ))}
      </ul>
    </nav>
  );
}

export default ArtistsList;

2번도 끝! 단순하게 리스트를 만들어놨을 뿐이다.
이제 각각의 li를 클릭하면 해당 ref가 있는 곳으로 이동하게하는 함수를 만들어보자.

3. 스크롤 이동 함수 만들기

// ArtistPage.tsx

const moveToArtist = (index: number) => {
  refArr[index]?.current?.scrollIntoView({
    behavior: "smooth",
    block: "center",
  });
};

return (
  <ArtistsList moveToArtist={moveToArtist} />  
);

1번에서 만들었던 컴포넌트에 위와 같은 함수를 만들어줬다.
이 글에서 가장 핵심적인 부분이다.
index에 맞는 refArr 값을 가져와서, 그 ref를 가진 컴포넌트가 보이도록 스크롤을 움직인다.
scrollIntoView()가 그 기능을 담당한다.
smooth는 부드럽게 이동하도록 만들어주고, center는 요소가 브라우저 중간에 위치하도록 만들어준다.
이 함수는 2번에서 만들었던 컴포넌트의 props로 들어간다.

// ArtistList.tsx

{artistArr.map((item, index) => (
  <li
    className="hover:cursor-pointer"
    key={index}
    onClick={() => moveToArtist(index)}
  >
    {item.name}
  </li>
))}

2번에서 만들었던 li의 onClick 속성에 이 함수를 넣어준다.
index를 인자로 가지고 실행하는데, ArtistPage와 ArtistList 컴포넌트 모두 같은 배열을 가지고 map을 사용했으므로 index를 통해 얻어내는 값이 같다.
따라서, ArtList에서 index를 인자로 주고 ArtistPage에 있는 moveToArtist 함수를 실행하더라도 다른 값을 참조하게되는 불상사가 발생하지 않는다.

완성이다.
이제 ArtistList에 있는 li를 하나 클릭하면 그 li의 인덱스에 맞는 ref(ArtistPage에 있는 요소)로 스크롤이 부드럽게 이동하게 된다.

useRef를 하나씩 만드는 것은 비효율적

필자가 useRef를 하나씩 써서 사용했는데, 이는 보여줘야할 요소가 늘어났을 때 굉장히 비효율적이다. 따라서 어떻게 useRef를 효율적으로 선언하고, 사용할지에 대해서 고민해볼 필요가 있다.
다음에 알아보기로 하고 여기서는 스크롤 이동 기능까지만 기록을 마치겠다.

profile
나를 믿는 사람들을, 실망시키지 않도록

1개의 댓글

comment-user-thumbnail
2023년 1월 9일

.

답글 달기