
이번에는 위의 사진처럼 쿼리 스트링을 이용해서 카테고리별로 목록을 보여주고 해당 버튼을 활성화하는 작업을 해보고자 한다.
react-router-dom에서 제공해주는 useLocation , useSearchParams에 대해 알고가야한다.
📌 useLocation
현재 URL의 위치 정버를 반환해주는 훅이다.
import { useLocation } from 'react-router-dom';
const location = useLocation();
console.log(location.search);
사용 방법은 다음과 같으며 우리는 퀄리 문자열을 반환해주는 search를 사용하면 된다.
그밖에도 현재경로 (pathname) , 상태 객체 (state) 등등도 반환할 수 있다.
📌 useSearchParams
앞서 받아온 쿼리 문자열을 쉽게 읽고 수정할 수 있게 도와주는 훅으로
URLSearchParams 객체와 함께 사용한다.
import { useSearchParams } from 'react-router-dom';
const [searchParams, setSearchParams] = useSearchParams();
const handleChange = (value) => {
const newSeachParams = new URLSearchParams(searchParams);
newSeachParams.set('category_id', value);
setSearchParams(newSeachParams);
};
위의 부분은 handleChange 함수가 쿼리 파라미터 값을 받으면
category_id 라는 쿼리 파라미터 값이 해당 값으로 변경되는 코드이다.
URLSearchParams 객체는 set말고도 get , delete 등을 사용해서
쿼리 문자열을 쉽게 읽고 수정할 수 있다.
과정
1. URL 설정
2. 화면 구현
우선 쿼리 파라미터를 사용하기 위해 당연히 URL를 정의해줘야 한다.
[api/books.api.ts]
import {httpClient} from './http';
interface FetchBooksParams {
category_id?: number;
}
interface FetchRES {
books: Book[];
}
export const fetchBooks = async (params: FetchBooksParams) => {
try {
const res = await httpClient.get<FetchRES>('/books/book_list', {
params,
});
return res.data;
}
// 생략...
이 코드는 특정 카테고리에 대한 데이터를 가져오기 위해 API를 설정하는 코드이다.
category_id를 /books/book_list URL 뒤에 추가해줬다.
[hooks/useBook.ts]
import {useLocation} from 'react-router-dom';
import {fetchBooks} from '../api/books.api';
import {useEffect, useState} from 'react';
const useCategory = () => {
const location = useLocation();
const [category, setCategory] = useState<Category[]>([]);
const setActive = () => {
const params = new URLSearchParams(location.search);
if (params.get('category_id')) {
setCategory(prev => {
return prev.map(item => {
return {
...item,
isActive: item.category_id === Number(params.get('category_id')),
};
});
});
} else {
setCategory(prev => {
return prev.map(item => {
return {
...item,
isActive: false,
};
});
});
}
};
useEffect(() => {
fetchCategory().then(category => {
if (!category) return;
const categoryWithAll = [
{category_id: null, category_name: '전체'},
...category,
];
setCategory(categoryWithAll);
setActive();
});
}, []);
useEffect(() => {
setActive();
}, [location.search]);
return {category};
};
export default useCategory;
여기가 현재 경로에서 category_id를 받아 해당하는 데이터를 가져오는 로직을 정의한 커스텀 훅이다.
useEffect 훅을 사용하여 현재 경로가 변경될 때마다 해당하는 데이터를 가져오도록 정의되어 있다.
또한 setActive을 통해 현재 경로와 전달받은 파라미터의 값이 같다면
활성화하는 로직도 추가되어 있다.
[components/Books/Bookfilter.tsx]
import useCategory from '../../hooks/useCategory';
import {useSearchParams} from 'react-router-dom';
const Bookfilter = () => {
const {category} = useCategory();
const [searchParams, setSearchParams] = useSearchParams();
const handleCategory = (id: number | null) => {
const newParams = new URLSearchParams(searchParams);
if (id === null) {
newParams.delete(QUERYSTRING.CATEGORY_ID);
} else {
newParams.set(QUERYSTRING.CATEGORY_ID, id.toString());
}
setSearchParams(newParams);
};
return (
<BookfilterStyle>
<div className="category">
{category.map(item => (
<Button
size="medium"
scheme={item.isActive ? 'primary' : 'normal'}
key={item.category_id}
onClick={() => handleCategory(item.category_id)}>
{item.category_name}
</Button>
))}
</div>
</BookfilterStyle>
);
};
여기가 화면에 보여주는 컴포넌트이다.
앞서 코드에서 정의하고 사용했던
훅들(useCategory ,useSearchParams)를 이용해
현재 경로와 쿼리 스트링(카테고리 id)을 가져온다
if (id === null) {
newParams.delete(QUERYSTRING.CATEGORY_ID);
} else {
newParams.set(QUERYSTRING.CATEGORY_ID, id.toString());
}
이 조건문을 통해 만약 id값이 null (전체)라면 쿼리 스트링을 삭제하고
그렇지 않다면 해당하는 쿼리 스트링으로 수정한다.
scheme={item.isActive ? 'primary' : 'normal'}
onClick={() => handleCategory(item.category_id)}
또한 버튼 컴포넌트 scheme에서 삼항 연산자를 통해
활성화, 비활성화 여부를 보여줘서 현재 어떤 카테고리가 선택된 것이지
시각적으로 보여주었다.
그동안 useLocation , useSearchParams를 사용한 적은 있으나
해당 훅들이 정확히 어떤 기능을 하고 왜 사용하는지는 잘 모르고 사용하고 있었는데 이번 계기로 어떤 기능을 하며 왜 사용하는 지 정확하게 알 수 있었다 ㅎㅎ
이 부분은 앞으로도 많이 사용할 거 같으니 이 블로그를 보며
복습하고 사용할 것이다.