오늘 한 일
- Intersection Observer API 이용하여 무한스크롤 구현
- 어제는 리액트 쿼리와 씨름했다면 오늘은 Intersection Observer와 씨름하겠거니 생각했는데 생각보다 간단하게 마무리.
글이 길어질 것 같아서 따로 작성했다.
- 어제는 리액트 쿼리와 씨름했다면 오늘은 Intersection Observer와 씨름하겠거니 생각했는데 생각보다 간단하게 마무리.
- BookSearchResultPage 컴포넌트 구현.
- 레이아웃까지 완성. 클릭시 BookDetailPage로 넘어감.
- BookDetailPage 컴포넌트 구현.
- BookSearchResultPage 와 거의 흡사. 다만 아직 디자인은 완성 못함.
무한 스크롤
// useInfiniteScroll.ts
import { useState, useEffect } from "react";
import { InfiniteQueryObserverResult } from "@tanstack/react-query";
interface ioProps {
hasNextPage: boolean | undefined;
fetchNextPage: () => Promise<InfiniteQueryObserverResult>;
}
const useInfiniteScroll = ({ hasNextPage, fetchNextPage }: ioProps) => {
const [target, setTarget] = useState<HTMLDivElement | null | undefined>(null);
const observerCallback = (entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting && hasNextPage) {
fetchNextPage();
}
});
};
useEffect(() => {
if (!target) return;
const observer = new IntersectionObserver(observerCallback);
observer.observe(target);
return () => observer.unobserve(target);
}, [observerCallback, target]);
return { setTarget };
};
export default useInfiniteScroll;
// BookSearchResultPage.tsx
const BookSearchResultPage: React.FC = (): JSX.Element => {
// 생략 ...
<div ref={setTarget} /> // ⭐️ 관찰 타겟
}
흠, 이 부분은 리팩토링을 해야 될 것 같다.
왜냐하면 좀 복잡하더라도 다른 방식이 더 나을 것 같다는 판단에서다. 스택 오버 플로우에 보면 이런 글이 있다.
ref is a special props in React. React will call the ref callback with the DOM element when the component mounts, and call it with null when it unmounts. ref updates happen before componentDidMount or componentDidUpdate lifecycle hooks. So in the first example, when ref props callback function gets executed, before the mount you are changing the state using setState, so component again starts mounting due to state change. And now your are in Infinite call loop.
리액트는 컴포넌트가 마운트 될 때 돔 요소의 ref 내부 콜백을 실행.
마운트 되기 전에 저 ref의 업데이트가 일어나기 때문에 -> 또 상태가 변경되어 다시 마운트가 일어나고 -> 반복되게 된다.
Note: ref is not a place to change the state. Refs provide a way to access DOM nodes or React elements created in the render method. As per the doc.
ref 내부에서 상태 변경하는 것은 옳지 않다. ref는 돔 노드에 접근할 때 주로 쓴다.
ref 안에서 useState 등으로 상태를 변경하면 안 된다는 것인데 오히려 무한 스크롤에서는 상관이 없는 걸까나? 아직은 내가 ref 에 대해서 잘 모르고 있는 것 같기도 하다. ref는 state와 비슷하지만 리렌더링을 일으키지 않기 때문에 요소를 참조할 때 많이 쓰는 걸로 알고 있는데 이런 방식으로 쓰는 게 맞나? 물론 저 요소를 참조해야 하는 건 맞긴 한데. 흠.
느낀 점
타입스크립트로 프로젝트를 진행하는 건 처음이라 다른 분들이 하신 프로젝트도 많이 보고 깃헙도 보고 있는데, 이런 타입을 달아놓으신 걸 봤다.
const BookSearchResultPage: React.FC = (): JSX.Element => {}
컴포넌트마다 react의 functional component임을 명시하고, 반환하는 게 JSX.Element 혹은 React.ReactNode 라고 명시하는 것. 이렇게 하신 분들이 꽤 계셔서 나도 이렇게 해야하나 싶어서 후다닥 모든 컴포넌트를 업데이트 했다가 아무래도 이건 아니지 않나 싶어서 검색을 또 해봤다. 이런 글이 나왔는데 한 마디로 말하자면 굳이? 라는 결론이다. 당연히 저렇게 명시하는 것은 명시적이라는 점이 장점이 있지만, 하나하나 달아줘야 하는 게 엄청나게 번거로운 작업이다. 게다가 타입스크립트가 함수를 읽고 다 유추할 수 있는데 굳이 저렇게 달지 않아도 된다는 것이다.
물론 다는 게 좋다는 의견도 있지만, Typescript가 먼저 나서서 너 리턴 타입 달아! 라고 경고하기 전까지는 나도 그냥 두는 방향으로 갈 것 같다.
'Recap > bookshelf' 카테고리의 다른 글
| [6일차] firebase로 책 데이터 관리, 해시태그 관리 (0) | 2024.01.29 |
|---|---|
| [5일차] 무한스크롤 리팩토링 및 Firebase Firestore로 데이터 관리 (0) | 2024.01.29 |
| [3일차] 무한 스크롤 구현(React Query, Intersection Observer API) -1 (0) | 2024.01.27 |
| [2일차] firebase로 회원가입, 로그인 구현 (0) | 2024.01.25 |
| [1일차] 🎉 bookshelf 프로젝트 시작하기 (0) | 2024.01.25 |