[Typescript] useRef를 다른 컴포넌트에 props로 전달할 때

박기영·2022년 8월 8일
5

Typescript

목록 보기
9/11

문제 상황

useRef를 하나의 컴포넌트에서만 사용하는게 아니라 props로 넘겨줘서 사용할 때가 있다.
예를들어, 메뉴바와 페이지 컴포넌트를 따로 만들어서 사용할 때가 있겠다.
메뉴를 눌러 페이지의 특정 위치로 이동하고 싶을 때 ref를 사용한다고 하자.
이 때, 메뉴바 컴포넌트에 ref를 줘야지 어떤 메뉴가 어디로 이동할지 알텐데...
다른 컴포넌트에 props로 전달할 때 어떻게 타입을 정의해야할까?

해결 방법

찾아본 결과, Arrow Function 사용법이 압도적으로 많았다.
필자는 컴포넌트를 만들 때 Regular Function을 많이 쓰기 때문에,
두 함수 모두 기록하려고 한다.

Arrow Function

//* ArtistPage.tsx *//

import ArtistsList from "./ArtistsList";

function ArtistPage() {
  const artistRef = useRef<HTMLDivElement>(null);
  
  // ... //

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

export default ArtistPage;

ref를 받아가는 컴포넌트에서 속성을 ref로 작성해야지만 된다.
필자는 이걸 모르고

// 틀린 방법
<ArtistsList artistRef={artistRef} moveToArtist={moveToArtist} />

이렇게 작성했다가, 1시간을 헤맸다.

// 맞는 방법
<ArtistsList ref={artistRef} moveToArtist={moveToArtist} />

꼭 ref를 사용하자.

//* ArtistsList.tsx *//

import React from "react";
import ArtistsList from "./ArtistsList";

interface PropsType {
  moveToArtist: (index: number) => void;
}

const ArtistsList = React.forwardRef<HTMLDivElement, PropsType>(
  ({ moveToArtist }, artistRef) => {
    return (
      <nav className="hidden lg:block top-1/2 left-1/2 sticky">
        <ul className="flex flex-col items-center font-semibold xl:text-xl">
          {artistArr.map((item, index) => (
            <li
              className="hover:cursor-pointer"
              key={index}
              onClick={() => moveToArtist(index)}
            >
              {item.name}
            </li>
          ))}
        </ul>
      </nav>
    );
  }
);

export default ArtistsList;

Arrow Function에서는 React.forwardRef를 사용한다.
위 코드는 필자의 코드에 적용한 것이라서 헷갈릴 수 있으니, 간단한 예시를 아래 작성해보겠다.

import React from "react";

const name = React.forwardRef<RefType ,PropsType>((props, ref) => {
	// ... //
})

PropsType은 interface나 type을 사용해서 선언한 것을 가져오면 되고, RefType은 부모 컴포넌트에서 작성한 ref의 타입을 써주면 된다.
잘 보면, 제네릭 타입 부분은 ref, props 순서인데, 파라미터 부분은 props, ref 순서이다.
forwardRef 자체가 저런 식으로 작성해야하는 것이므로, 이상하더라도 그렇구나하고 받아들이자.

Regular Function

//* ArtistPage.tsx *//

import ArtistsList from "./ArtistsList";

function ArtistPage() {
  const artistRef = useRef<HTMLDivElement>(null);
  
  // ... //

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

export default ArtistPage;
//* ArtistsList.tsx *//

interface ArtistsListPropsType {
  artistRef: React.ForwardedRef<HTMLDivElement>;
}

function ArtistsList({ artistRef }: ArtistsListPropsType) {
  return (
	// ... //
  );
}

export default ArtistsList;

Regular Function에서는 흔히들 Typescript에서 props에 타입을 알려주는 방법과 동일하다.
interface의 artistRef의 타입을 살펴보자.
div에 대한 ref기 때문에 HTMLDivElement를 사용했다는 점에 주의!

React.ForwardedRef<HTMLDivElement>;

위와 같은 방식으로 타입을 정의해주면 된다.

주의

위에서 이미 설명했지만 Arrow Function일 때와, Regular Function일 때 눈치 못 채고 실수할 수 있는 부분이 있다.
부모 컴포넌트에서 자식 컴포넌트로 ref값을 넘겨 줄 때 아래와 같이 작성해줘야한다.

// Arrow Function
<ArtistsList ref={artistRef} moveToArtist={moveToArtist} />

// Regular Function
<ArtistsList artistRef={artistRef} moveToArtist={moveToArtist} />

전자는 ref라는 속성을 반드시 기입해줘야하고, 후자는 그럴 필요가 없다.

참고 자료

참고 자료 1
참고 자료 2
참고 자료 3
참고 자료 4
참고 자료 5

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

0개의 댓글