포트폴리오를 만들면서 화면 스크롤을 구현하다가 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:refis not a prop. Trying to access it will result inundefinedbeing 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 에러는 발생하지 않을 것이다.