어느 프로젝트를 보든 대부분 포함되어있는 기능 중 하나가 아마 필터링 기능이지 않을까 싶다.
이전에 내가 필터링 기능을 구현할 때는 클라이언트단에서 서버로부터 받은 데이터를 가지고 필터링을 해주거나, 프론트엔드에서 원하는 데이터만 요청시 서버단에서 그에 대한 필터링된 데이터만 응답해주는 방법이었다.
✅ 이번엔 useQuery의 Select 옵션을 사용해보자.
select 옵션을 사용하면 쿼리함수가 반환하는 데이터를 변환시킬 수 있다.
React Query는 Memoization을 통해 불필요한 연산을 줄이는 최적화를 해준다.
💡 메모이제이션 : 컴퓨터 프로그램이 동일한 계산을 반복해야 할 때, 이전에 계산한 값을 메모리에 저장함으로써 동일한 계산의 반복 수행을 제거하여 프로그램 실행 속도를 빠르게 하는 기술
React Query는 Select 함수를 삼중 등호로 비교하며, Select 함수는 데이터와 함수가 모두 변경되었을 경우에만 실행된다.
마지막으로 검색한 데이터와 동일한 데이터이고 Select 함수에도 변동이 없으면 Select함수를 재실행하지 않는 것이 React Query의 최적화라고 할 수 있다.
따라서 Select함수에는 안정적인 함수가 필요하고, 메모이제이션하는 React의 useCallback
을 사용하면 된다.
💡 매우좋은 글 👉 React Query Data Transformations
✅ 간단한 예를 통해 알아보자.
select 기능을 통해서 내가 원하는 카테고리를 클릭시 해당 카테고리에 포함되는 데이터만 출력되게 만들 것이다.
1️⃣ filter의 상태값이 all
이라면.
2️⃣ filter의 상태값이 all
이 아니라면.
즉, filtering 기능이 동작한다면 selectFn 함수가 실행되고, 이때 인자로 useQuery로 받은 데이터와 filter 상태값이 전달된다.
useCallback
을 사용하여 Select 함수에 변동이 없으면 Select함수를 재실행하지 않게 만들어 최적화.
// useStaff.tsx (커스텀훅)
export const useStaff = (): UseStaff => {
const [filter, setFilter] = useState('all');
const selectFn = useCallback( // 2️⃣ 번
(unfilterdStaff) => filterByTreatment(unfilterdStaff, filter),
[filter],
);
const { data: staff } = useQuery(queryKey, queryFn, {
select: filter !== 'all' ? selectFn : undefined, // 1️⃣ 번
});
return { staff, filter, setFilter };
}
3️⃣ filterByTreatment
은 인자로 받은 데이터를 원하는 카테고리에 맞게 출력되게끔 동작하는 필터링 함수.
staff
: 전달받은 전체 데이터treatmentName
: 전달 받은 데이터 속 데이터의 종류// util.tsx (필터링함수)
export const filterByTreatment = (staff, treatmentName) => {
return staff.filter((person) =>
person.treatmentNames
.map((t) => t.toLowerCase())
.includes(treatmentName.toLowerCase()),
);
}
4️⃣ 데이터 종류가 변경되는 원리는 실제로 화면에 보이는 컴포넌트에서 Radio태그를 통해 카테고리를 클릭할 경우 클릭한 값에 따라 변경되는 것이다.
5️⃣ Radio태그에 출력될 값은 filter, 클릭시 실행되는 값은 setFilter.
// AllStaff.tsx (화면에 출력되는 컴포넌트)
export const AllStaff = () => {
const { staff, filter, setFilter } = useStaff();
const treatments = useTreatments();
return (
<RadioGroup onChange={setFilter} value={filter}> // 5️⃣ 번
<Radio value="all">All</Radio>
{treatments.map((t) => (
<Radio key={t.id} value={t.name}>
{t.name}
</Radio>
))}
</RadioGroup>
);
}