當圖鑑或交換列表的資料變多時,除了滑動順暢,更重要的是「找得快」。今天我們要在畫面上加上搜尋框與過濾條件 (例如:過濾性別、地區、特定標籤),讓使用者能精準定位寶可夢!
在實作過濾功能時,我們通常面臨兩種選擇:
| 過濾策略 | 運作方式 | 優劣勢與適用情境 |
|---|---|---|
| 前端過濾 (Frontend Filtering) | 先把資料庫「所有」資料一次抓回前端,再用 JS 的 array.filter() 在瀏覽器端做篩選。 |
優點:過濾極快,不需重打 API。缺點:如果資料有幾十萬筆,初次載入會非常慢,且耗費手機記憶體。適用:總資料量小於一千筆的系統。 |
| 後端過濾 (Backend Filtering) | 前端將搜尋關鍵字傳給後端,由後端的 SQL (WHERE name LIKE '%皮卡丘%') 執行過濾後,只回傳需要的資料。 |
優點:前端永遠只收到必要的輕量資料,節省頻寬與記憶體。缺點:每次打字過濾都要送 API 請求 (通常需搭配 Debounce 防抖)。適用:大型資料庫、企業級系統 (本專案採用此架構的變體設計)。 |
在我們的系統中,由於搭配了 React Query 的強大快取,我們把過濾條件交給 Hook 統一管理,完美結合了後端查詢與前端快取的優勢。
我們設計了一個 useFilters 來管理使用者的搜尋條件,並將這些條件無縫傳遞給 React Query:
import { useState, useMemo } from 'react';
import { TextField, Box } from '@mui/material';
// 假設這是在圖鑑頁面 (PokedexPage.tsx)
const PokedexPage = () => {
// 1. 統一管理所有過濾狀態
const { filters, updateFilter } = useFilters();
// 2. 將過濾條件餵給 React Query
// 當 filters 改變時,queryKey 也會變,自動觸發背景 API 重新抓取或從快取拿資料!
const { data, isLoading } = useDetailedPokemon(filters);
return (
<Box>
{/* 搜尋框:輸入文字即時更新 filter 狀態 */}
<TextField
placeholder="搜尋寶可夢名稱或編號..."
value={filters.searchText || ''}
onChange={(e) => updateFilter('searchText', e.target.value)}
fullWidth
sx={{ mb: 2 }}
/>
{/* 渲染過濾後的資料列表 */}
{isLoading ? <div>搜尋中...</div> : (
<div>共找到 {data?.pages[0]?.total} 筆符合條件的寶可夢!</div>
)}
</Box>
);
};

輸入框搜尋「伊布」,下方立刻過濾出對應的寶可夢!
結合 React Window 帶來了順暢的滾動,結合 React Query 與 Filters 帶來了極速的搜尋,前端的「進階功能與效能優化 (階段四)」正式完成!鐵人賽即將進入最後衝刺,明天我們要邁入階段五:將心血結晶部署上雲端!