https://pokeapi.co/api/v2/pokemon/?offset=0&limit=20
요청 데이터를 확인 해보면 next 라는 URL 이 있는 것을 확인 할 수 있는데 next 로 넘어가게 되면 offset의 값이 증가하고 다음 목록의 포켓몬을 20 개 반환 하는 것을 알 수 있습니다.
포켓몬을 가져올 때 offset 과 limit 의 값을 설정 하여 자신이 받아오고 싶은 만큼의 포켓몬을 요청하여 결과 값을 받아올 수 있습니다.
이전에 보여드린 getAllPokemonAPI 라는 api 요청 function 은 다른 function 들과 다르게 매개 변수로 offset 을 받아오는데, 스크롤을 화면의 끝까지 내렸을 때 "react-intersection-observer" 가 이것을 감지하여 offset 의 값을 증가 시키기 위함 입니다.
function Pokemon() {
const LIMIT_PER_PAGE = 20; // 요청 URL의 offset 증가를 위한 상수
// 포켓몬을 담기 위한 빈 배열 선언
const [pokemonList , setPokemonList] = useState([]);
const [page, setPage] = useState(0);
const [ref, inView] = useInView(true);
useEffect(() => {
async function fetchData() {
try {
// offset은 프로그램 시작 후 증가 -> -20 에서 0 으로 변경.
const offset = (page - 1) * LIMIT_PER_PAGE;
// getAllPokemonAPI 에 offset 전달.
const pokemonData = await getAllPokemonAPI(offset);
// 기존 데이터와 새로운 데이터가 병합 된 newList 반환.
setPokemonList((prevList) => {
const newList = [...prevList, ...pokemonData];
return newList;
});
} catch (error) {
console.error('Error:', error);
}
}
// fetchData() 호출.
fetchData();
// dependency 를 page 로 설정 -> page 가 변경 될 때마다 실행.
}, [page]);
// 페이지 로드시 uploadData 함수 호출
useEffect(() => {
const uploadData = () => {
// inView === true page 이전 값에서의 증가.
if (inView) {
setPage((prevPage) => prevPage + 1);
}
};
// uploadData() 호출
uploadData();
// inView 가 감지되면 uploadData() 호출
}, [inView]);
return(
<div>
{pokemonList.map((pokemon, index) => (
// pokemonModal = 디테일한 정보 전달
<PokemonCard
key = {index}
pokemon = {pokemon}
.
.
.
/>
))}
</div>
);
}
export default Pokemon;
스크롤을 내릴 때마다 API 를 요청하고, 요청된 추가적인 정보를 화면에 출력하기 위한 코드입니다. (코드가 길고 복잡한 점은 개선해나가겠습니다..!)
포켓몬스터 공식 홈페이지의 도감 목록 입니다.기본적으로 포켓몬을 카드의 형태로 출력하고, 카드를 클릭 하였을 때 포켓몬의 디테일한 정보를 보여줍니다.
좋은 방식이라고 생각 했으나, 그대로 만드는 것 보다 다른 방식으로 하는 것이 공부가 될 것같아 Modal 방식을 사용해 보았습니다.
직접 Modal 창을 만들어 보는것과 라이브러리를 사용하는 것을 고민하다가,
라이브러리를 사용해보았습니다.
npm i flowbite-react
이것저것 보다가 찾은 flowbite-react 에 대한 Docs 입니다. 설명을 보면
Flowbite React는 핵심 Flowbite 구성 요소를 기반으로 하고 React 구성 요소와 상호 작용 처리 기능으로 구축된 무료 오픈 소스 UI 구성 요소 라이브러리입니다.
이 라이브러리에는 Navbar, 드롭다운, 모달 및 사이드바와 같은 수백 개의 대화형 요소가 포함되어 있으며 모두 React에서 처리하고 Tailwind CSS의 유틸리티 클래스를 기반으로 합니다.
tailwind를 사용하고 Modal 창을 구현하기 위한 저에게 꼭 맞다고 생각하여 flowbite-react 를 사용해보았습니다.
Docs 에 따라 tailwind.config 를 설정 해주신 후
flowbite-Modal 문서에서 Modal에 대한 내용을 본 뒤 프로젝트에 어울리는 것이 무엇인지 판단하고 사용하시면 될 것 같습니다.
Backdrop dismissible Modal은 창을 띄운 후 Modal이 아닌 바깥 부분을 클릭하면 Modal 창이 닫히게 됩니다. 기본적인 Default Modal 방식 보다 사용자에게 좋은 경험을 줄 수 있을 것 같아 채택하였습니다.
기본 방식과 다른 점은
props.setOpenModal('dismissible')
<Modal dismissible show={props.openModal === 'dismissible'}/>
props.setOpenModal()을 dismissible 로 변경, Modal 컴포넌트의 props 에 dismissible 을 추가 해주시면 됩니다.
또한 Modal 창을 클릭 하였을 때 뒷 배경의 Scroll 을 막기 위해 코드를 추가 하였습니다.
const [openModal, setOpenModal] = useState();
useEffect(() => {
if (openModal) {
// 모달이 열렸을 때 body에 오버플로우 숨김 스타일 추가
document.body.style.overflow = "hidden";
} else {
// 모달이 닫혔을 때 body 스타일 초기화
document.body.style.overflow = "auto";
}
}, [openModal]);
또한 조건부 렌더링을 사용하여 Modal 이 열린 상태라면 뒷 배경이 어두워지게 작성 하였습니다.
const [openModal, setOpenModal] = useState();
{openModal && (
<div
className="fixed inset-0
z-50 flex items-center
justify-center
bg-black bg-opacity-70
">
{pokemonDetail}
</div>
)}
결과물

openModal
