다른 컴포넌트에 Ref 전달 (forwardRef)

goodjam92·2023년 3월 4일
0

React

목록 보기
1/7
post-thumbnail

포트폴리오를 만들면서 화면 스크롤을 구현하다가 useRef에 대한 에러를 만났고, 이를 해결하기 위해 열심히 구글링해서 찾은 해결 방법을 정리해보기로 한다.

환경

  • Next JS (v13.2.3)
  • React (v18.2.0)
  • Typescript (v4.9.5)
  • styled-components (v5.3.8)
  • eslint (8.35.0)

nav버튼을 누르면 내가 원하는 section요소로 자동으로 스크롤되어 화면이 이동하는 기능을 구현하려고 하였다.

export default function LandingPage () {
 const sectionRef = useRef<HTMLDivElement>(null); 

 const onClick = () => {
   sectionRef.current?.scrollIntoView({ behavior: "smooth", block:"start" })
 };

 return (
   <div>
     <nav>
 	   <button onClick={onClick}>move</button>  
     </nav>
   	 <div ref={sectionRef}>
   	   이곳으로 이동
  	 </div>
  	 <div>
       다른 화면
   	 </div>
   </div>
   )
};

위와 같이 구현하고나면 버튼을 눌렀을 때 scrollIntoView 메서드로 인해 화면이 refdiv로 스크롤링 된다.
이런식으로 하나의 컴포넌트에 모든걸 구현하는 것이라면 뭐 문제없이 간단하게 구현이 가능하다.

문제는 useRef를 다른 컴포넌트에서 받아오거나 넘겨줘야 할 때에 발생한다.

export default function LandingPage () {
  const contentRef = useRef<HTMLDIVElement>(null);
  
  const onClickToMoveContent = () => {
   contentRef.current?.scrollIntoView({behavior: "smooth", block:"start" })
  }
  
  return (
    <ContentsWrap>
      <Nav onClick={onClickToMove}/>
      <Section> 
        <Content ref={contentRef}/>
      </Section>
    </ContentsWrap>
    )
}
---------------------------------------------------------
// Content.tsx
  
interface ContentProps {
 ref: Ref<HTMLDivElement>; 
}

const Container = styled.div`
  height: 100%;
  width: 100%;
`;

const Content: FunctionComponent<ContentProps> = function ({ ref }) {
    return (
      <Container ref={ref}>Project</Container>
  );
}
export default Content;

error!
1. Content: ref is not a prop. Trying to access it will result in undefined being returned. If you need to access the same value within the child component, you should pass it as a different prop.
2. Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

이러한 두 개의 에러가 날 반겨줬었는데, 검색해보니 직접적으로 ref를 일반적인 prop으로 사용할 수 없다고 한다. 그래서 컴포넌트에서 위 에러메세지에 나와있듯이 ref prop을 사용하려면 forwardRef()라는 함수를 사용해야 한다고 한다.

forwardRef()에 대한 공식 문서를 보니 렌더링 함수를 인자로 받아들이고, 이 함수는 props, ref를 호출한다고 되어있었다.

그래서 forwardRef()를 사용하여 아래와 같이 <Content /> 컴포넌트의 코드를 변경해보았다. 이 함수에는 refprops를 인자로 받는데 내가 만든 컴포넌트는 부모로부터의 props을 받는 것이 없었기에.. props는 사용하지 않기 위해 _를 사용하였다

// Content.tsx
const Container = styled.div`
  height: 100%;
  width: 100%;
`;

// props를 받지 않는 컴포넌트라서 '_'를 사용.
const Content = forwardRef<HTMLElement>((_, ref: Ref<HTMLElement>) => {
    return (
      <Container ref={ref}>Project</Container>
  );
}
export default Content;

이렇게 변경하여 동작을해보니 내가 원하는대로 버튼을 누르면 원하는 부분으로 화면이 자동 스크롤 되는 것을 확인하였다.

그런데 여기서 또 하나.. 웹에서는 오류가 없지만 VS에서 에러 표시가 떴는데..

Component definition is missing display nameeslintreact/display-name

eslint에 대한 오류였다. display name을 찾을 수 없다는 내용같은데.. 해당 내용이 github에 있었다. 오류내용

React 구성 요소 정의에서 누락된 displayName을 허용하지 않음( react/display-name)

이러한 내용이었는데 이 부분에 대한 해결방법은 아래와 같다.

  • eslint.json 옵션 추가
"react/display-name": ["enable", { "ignoreTranspilerName": true }]
  • 일반 함수로 작성
function Content(_: any, ref: Ref<HTMLDivElement>) {
  return (
    <Container ref={ref}>Project</Container>
  );
}

export default forwardRef(Content);
  • 화살표 함수 사용 시 (displayName 작성)
const Content = forwardRef<HTMLElement>((_, ref: Ref<HTMLElement>) => {
    return (
      <Container ref={ref}>Project</Container>
  );
}

Content.displayName = "Content";

export default Content;

아니면 VS code는 에러가 발생하였을 때 Quick fix로 해결 방법을 제시해주는데 그 중에 eslint의 규칙을 무시하여주는 주석을 다는 방법을 사용해도 된다.

// eslint-disable-next-line react/display-name (함수 위에 작성)

이중에서 하나의 방법을 선택해서 사용하면 VisualStudio Code에서 eslint 에러는 발생하지 않을 것이다.

profile
습관을 들이도록 노력하자!

0개의 댓글