다양한 커스텀 훅(Custom Hook)을 설계, 구현 및 문서화 할 것
useFetchMovies
useFavoriteMovies
useSearchMovies
usePagination
커스텀 훅을 만들기전에 의미를 알아보자
커스텀: 관습이라는 사전적 의미이지만, 여기서는 본인의 상황에 맞는 개인 맞춤 정도로 보면 된다
훅: 함수라고 보면 된다(useXxxx
)
즉, 개발하는데에 있어 여기저기서 공통적으로 쓰이는 코드를 맞춤으로 설계하여 반복작업을 줄이는 행위이다
보통의 프론트 작업에서 데이터를 가져와야 하는 경우,
useEffect
에서 빈배열을 두어, 초기렌더링 시 데이터를 요청- 응답이 올 때 까지 로딩화면 표시
- 제대로 데이터를 가져올 경우, 데이터를 렌더링
- 에러발생의 경우 후속처리
위의 작업을 반복적으로 하게 된다
이와 같은 반복을 줄이기 위해서는 커스텀 훅을 만드는 것이 좋다
// src/hook/useFetchMovies.js
import { useEffect, useState } from "react";
const BASE_URL = "https://yts.mx/api/v2";
const useFetchMovies = (path, params) => {
const [isLoading, setIsLoading] = useState(false);
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
setIsLoading(true);
let url = `${BASE_URL}${path}`;
if (params) {
const searchParams = new URLSearchParams(params);
url += "?" + searchParams.toString();
}
fetch(url, { method: "GET" })
.then((res) => res.json())
.then((json) => {
// console.log(json.data);
setData(json.data);
})
.catch((err) => setError(err))
.finally(() => {
setIsLoading(false);
});
}, [path, params]);
return { data, error, isLoading };
};
export default useFetchMovies;
path
: 베이스 url에다가 요청을 보낼 경로(리스트면 리스트, 디테일이면 디테일)
params
: 요청 시, 물음표(?) 뒤에 오는 간단한 정보들을 담는다(보통 어떤 아이디, 갯수 등)
현재는 GET
요청에 한해 돌아가는 코드를 구현해보았다
더욱 살을 붙여 다른 요청에도 방어가 가능한 코드로 발전시키면 좋을 것 같다
// App.js
const { data, isLoading, error } = useFetchMovies(
"/list_movies.json",
`minimum_rating=7.0&sort_by=year&page=${temp.current}`
);
게시판등에서, 총게시물이 너무 많을 경우 infinite scroll방식을 채택할 수 있지만 전통적으로는 페이지네이션을 많이 쓴다
이 기능을 구현하려면, 얼마나 많은 데이터수를 가지고 있는지와 한 화면에 뿌려줄 데이터 갯수를 바탕으로 페이지네이션을 구현해야 한다
// src/hook/usePagination.js
import { useState } from "react";
const usePagination = (totalCount = 1, itemsPerPage = 1) => {
const [currentPage, setCurrentPage] = useState(1);
const maxPage = Math.ceil(totalCount / itemsPerPage);
const next = () => {
setCurrentPage((currentPage) => Math.min(currentPage + 1, maxPage));
};
const prev = () => {
setCurrentPage((currentPage) => Math.max(currentPage - 1, 1));
};
const jump = (page) => {
const pageNumber = Math.max(1, page);
setCurrentPage(Math.min(pageNumber, maxPage));
};
return { next, prev, jump, currentPage, maxPage };
};
export default usePagination;
총 데이터 갯수를 페이지당 렌더링할 갯수로 나눈 값을 올림 하면 총 페이지의 개수가 된다
또한 페이지 이동의 경우, useState
로 선언한 currentPage
를 컨트롤 해주면 된다
// src/components/Pagination.js
const Pagination = ({ next, prev, jump, currentPage, maxPage }) => {
const computedStyle = (page) => {
if (page === currentPage) {
return {
color: "red",
};
}
};
const computedStartPages = () => {
if (currentPage > 5) {
return Math.floor((currentPage - 1) / 5) * 5;
}
return 0;
};
const computedEndPages = () => {
if (currentPage > 5) {
return (Math.floor((currentPage - 1) / 5) + 1) * 5;
}
return 5;
};
return (
<PaginationWrapper>
<button onClick={() => prev()}>prev</button>
{Array(maxPage)
.fill()
.map((_, idx) => idx + 1)
.slice(computedStartPages(), computedEndPages())
.map((v) => (
<button style={computedStyle(v)} key={v} onClick={() => jump(v)}>
{v}
</button>
))}
<button onClick={() => next()}>next</button>
</PaginationWrapper>
);
};
export default Pagination;
현재 익숙하지 않은 리액트의 개발을 하려다 보니 과제에 미흡한 점이 많이 있다
추후에 보강하여 개발을 해두어야겠다
본 후기는 유데미-스나이퍼팩토리 10주 완성 프로젝트캠프 학습 일지 후기로 작성 되었습니다.