netflix-clone 프로젝트에서 carousel을 라이브러리써서 만들었는데, 생각보다 커스텀이 어려워서 마음에 들지않았다. 이번에 시간이 생겨서 직접 구현하는 도중 useRef - current null 문제에 직면하게 되었고, 해당 문제를 해결하며 useRef의 특성을 알게되었다. 그 과정을 적을 것이다.
각 카드의 width를 받아와서 부모 컴포넌트(CarouselTwo)에서 계산을 해주기위해서 ref 요소를 context api를 통해 공유하는 상황이다.
CardItem 컴포넌트에서 부모컴포넌트에서 받아온 ref를 지정해주고 data가 들어왔을 때 ref 값이 바뀌어있겠지..? 생각하고 console.log를 찍어보았는데
const CardItem: React.FC<SliderItemProps> = ({ movie, ...props }) => {
const context = React.useContext<Partial<SliderContextProps>>(SliderContext);
const { elementRef } = context;
console.log(elementRef) // current: null
console.log(context) // elementRef : current : ~~~
return (
<CardItemContainer ref={elementRef} {...props}>
<img src={`${TMDB_BASE_IMG_URL}${movie.backdrop_path}`} alt={movie.name}/>
</CardItemContainer>
)
}
이상하게도 context의 elementRef에 접근해서 console을 찍으면 null이고, context로 접근했을 때는 해당 div태그 요소를 받아왔다.
useRef의 특징중에 아래의 두가지 특징이 있다.
1. Ref가 변해도 re-render 되지않는다.
2. re-render되어도 Ref는 유지된다.
DOM 요소에 접근만 하면된다고 생각하고 두가지 특징을 잊고 있었다..!
그럼, data가 들어와서 다시 re-render 될 때 ref는 data가 들어오기 전에 null인 값을 갖고 있기때문에 변경이 되지 않는 것이다..
그래서 위의 CardItem의 부모 컴포넌트의 조건문을 변경하였다.
Before
<>
<CarouselTwo>
{loading ? <>로딩중..</> :
movieData.results.map((movie: MovieDataProps) => (
<CardItem movie={movie} key={movie.id}/>
)
)}
</CarouselTwo>
</>
After
데이터가 있을 때 CardItem을 render하게끔 수정
<div>
{loading && !movieData?.results.length ?
<>로딩중</>
:
<CarouselTwo>
{
movieData.results.map((movie: MovieDataProps) => (
<CardItem movie={movie} key={movie.id}/>
)
)
}
</CarouselTwo>
}
</div>
useRef의 개념만 이해를 하고, 직접 코드로 hook을 사용하여 구현을 많이 하지 못했었는데 이번 기회를 통해서 개념적으로 알던 것들을 직접 구현하고 문제에 직면하면서 해결할 수 있어서 좋았습니다 : )
https://stackoverflow.com/questions/56541342/react-hooks-why-is-current-null-for-useref-hook