포트폴리오를 만들면서 화면 스크롤을 구현하다가 useRef
에 대한 에러를 만났고, 이를 해결하기 위해 열심히 구글링해서 찾은 해결 방법을 정리해보기로 한다.
환경
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
메서드로 인해 화면이 ref
의 div
로 스크롤링 된다.
이런식으로 하나의 컴포넌트에 모든걸 구현하는 것이라면 뭐 문제없이 간단하게 구현이 가능하다.
문제는 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 inundefined
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 />
컴포넌트의 코드를 변경해보았다. 이 함수에는 ref
와 props
를 인자로 받는데 내가 만든 컴포넌트는 부모로부터의 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)
이러한 내용이었는데 이 부분에 대한 해결방법은 아래와 같다.
"react/display-name": ["enable", { "ignoreTranspilerName": true }]
function Content(_: any, ref: Ref<HTMLDivElement>) {
return (
<Container ref={ref}>Project</Container>
);
}
export default forwardRef(Content);
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 에러는 발생하지 않을 것이다.