April 19, 2021
무한스크롤을 위해 Intersection Observer API
를 사용하였습니다.
스크롤이 최상단으로 와서 setTarget
이 화면에 노출되면, 데이터를 불러오는 함수를 실행시킵니다
<StyledChatting ref={scrollBarRef} onScroll={handleScroll} id="scroll-test">
{/* // 페이지 끝 감지 */}
<div ref={setTarget} />
{messages.reverse().map(message => (
<dl key={message.date}>
<dt>
<span>{message.date}</span>
</dt>
{message.messages.reverse().map(chat => (
<ChattingItem key={chat.createdAt} chat={chat} onClickProfile={onClickProfile} />
))}
</dl>
))}
</StyledChatting>
데이터 요청 함수가 실행되면,
현재 scrollHeight
를 prevScrollHeight
에 저장하고
새로운 데이터를 불러옵니다(onFetchMessageMore(lastMessageId)
)
const handleFetchMessageMore = useCallback(() => {
/**
* 1. 스크롤이 최상단에 닿여서 데이터를 불러오기전에 현재 scrollHeight를 저장한다
* 2. 새로운 데이터를 불러왔을때 scrollHeight에서 저장해둔
scrollHeight를 뺀값이 현재 스크롤 위치
*/
setPrevScrollHeight(scrollBarRef.current?.scrollHeight);
onFetchMessageMore(lastMessageId);
}, [lastMessageId]);
그리고 useEffect
를 이용해 messages
의 변화를 관찰합니다. 변화가 있다는 것은 새로운 데이터를 불러왔다는 의미이므로 그때 스크롤의 위치를 현재 scrollHeight - 과거 scrollHeight(prevScrollHeight)
로 이동시키면 새로운 데이터를 불러오기 전 마지막 채팅이 있던 곳에 스크롤 위치를 유지하게됩니다
스크롤 위치 이동 후 과거 스크롤 위치는 null
로 바꿔줍니다
setPrevScrollHeight(null);
prevScrollHeight
이 있을경우(무한스크롤을 통해 새로운 데이터를 가져오는 경우)에는 스크롤 위치를 현재 scrollHeight - 과거 scrollHeight(prevScrollHeight)
로 유지하고 없을경우(새로운 채팅을 보내거나 첫 렌더링시)에는 에는 스크롤을 맨아래로 이동시켜주기 위해서입니다
useEffect(() => {
if (prevScrollHeight) {
onScrollTo(scrollBarRef.current?.scrollHeight - prevScrollHeight);
return setPrevScrollHeight(null);
}
onScrollTo(scrollBarRef.current?.scrollHeight - scrollBarRef.current?.clientHeight);
}, [messages]);