[ Recommend.tsx ]
import axios from 'axios';
import { Banner } from '@/components';
import { useEffect, useState } from 'react';
import classes from './Recommend.module.scss';
import { FoodList } from '@/components/FoodList/FoodList';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { Pagination } from '@/components/Pagination/Pagination';
import { ScrollButton } from '@/components/Button/ScrollButton/ScrollButton';
export default function Recommend() {
useDocumentTitle('슬기로운 N밥 생활 | 추천');
const postsPerPage = 20;
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const API_URL = `https://api.odcloud.kr/api/15097008/v1/uddi:1e5a6f2e-3f79-49bd-819b-d17541e6df78?page=3&perPage=80&serviceKey=${
import.meta.env.VITE_SERVICE_KEY
}`;
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const { data } = await axios.get(API_URL);
setPosts(data.data);
} catch (error) {
console.error(error);
}
setLoading(false);
};
fetchData();
}, [API_URL, currentPage]);
// Get current posts
const indexOfLastPost = currentPage * postsPerPage;
const indexOfFirstPost = indexOfLastPost - postsPerPage;
const currentPosts = posts.slice(indexOfFirstPost, indexOfLastPost);
// Change page
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
return (
<>
<Banner />
<h1 className={classes.title}> 서울 맛집 추천</h1>
<FoodList posts={currentPosts} loading={loading} />
<Pagination
postsPerPage={postsPerPage}
totalPosts={posts.length}
paginate={paginate}
/>
<ScrollButton />
</>
);
}
useEffect hook을 사용하여, 컴포넌트가 마운트될 때와 API_URL 혹은 currentPage가 변경될 때마다
API를 호출하여 데이터를 가져옵니다.
[FoodList.tsx ]
import { useEffect, useState } from 'react';
import { Card } from '../Card';
import classes from './FoodList.module.scss';
import spinner from '/public/assets/loading.svg';
interface Food {
지역명: string;
식당명: string;
'음식이미지(URL)': string;
}
interface Props {
posts: Food[];
loading: boolean;
}
export function FoodList({ posts, loading }: Props) {
const [showCards, setShowCards] = useState<boolean>(false);
useEffect(() => {
// 페이지가 바뀔 때마다 0.1초 뒤에 카드 보이기
setShowCards(false);
const timer = setTimeout(() => {
setShowCards(true);
}, 100);
return () => clearTimeout(timer);
}, [posts]);
if (loading || !showCards) {
return (
<div role="alert" className={classes.loading}>
<img src={spinner} alt="로딩 이미지" />
</div>
);
}
return (
<div className={classes.store}>
{showCards &&
posts.map((food: Food, index: number) => (
<Card key={index} className={classes.card}>
<div className={classes.storeBox}>
<p className={classes.LocalName}>{food['지역명']}</p>
<p className={classes.storeName}>{food['식당명']}</p>
<img
src={food['음식이미지(URL)']}
alt={'음식이미지'}
width={150}
height={150}
/>
</div>
</Card>
))}
</div>
);
}
이 컴포넌트는 posts 배열에서 음식 데이터를 가져와 Card 컴포넌트로 표시합니다. 이 때, showCards 상태를 사용하여 카드가 보여지는 시점을 조절합니다. useEffect를 사용하여 posts 배열이 변경될 때마다 showCards 상태를 false로 설정하고, 0.1초 후에 true로 설정합니다. 이로 인해 페이지가 변경될 때마다 카드가 부드럽게 보이게 됩니다.
또한, loading 상태도 고려하여 로딩 중일 때는 로딩 스피너를 표시합니다. 이를 위해 loading 값이 true이거나 showCards 값이 false일 때 로딩 스피너가 표시되도록 if 문을 사용합니다. 이를 통해 데이터가 로드되기 전에는 로딩 스피너가 보여지고, 카드가 나타날 때까지 부드럽게 전환됩니다.
🧐 결과
'Project' 카테고리의 다른 글
채팅 구현시 구글 로그인된 정보값을 가져오지 못하는 문제 (0) | 2023.04.09 |
---|---|
로그인한 사용자별 다른 프로필 보이게 하기 (0) | 2023.04.06 |
접근성을 고려하여 Tab 키 만을 이용해서 작동 구현 (0) | 2023.04.01 |
채팅 기능 메세지와 이미지 업로드 구현 (0) | 2023.03.24 |
Router 연결 관련 문제 (0) | 2023.03.22 |