React使用PokeAPI按ID实现宝可梦地区筛选 兼容类型筛选与分页
宝可梦图鉴地区筛选功能实现方案
你原本的ID区间匹配思路完全可行,只需要对现有数据拉取逻辑做少量修改即可,同时可以兼容现有类型筛选、分页功能,具体实现步骤如下:
前置准备
首先维护一份地区和对应宝可梦ID区间的映射表,覆盖所有主线世代即可:
const REGION_ID_RANGE = { all: { start: 1, end: 1025 }, // 全国图鉴默认覆盖到帕底亚世代 kanto: { start: 1, end: 151 }, johto: { start: 152, end: 251 }, hoenn: { start: 252, end: 386 }, sinnoh: { start: 387, end: 493 }, unova: { start: 494, end: 649 }, kalos: { start: 650, end: 721 }, alola: { start: 722, end: 809 }, galar: { start: 810, end: 898 }, paldea: { start: 899, end: 1025 } };
新增一个selectedRegion状态存储当前选中的地区,默认值设为all;同时在地区、类型筛选器的change事件里,把分页页码page重置为0,避免出现页码越界问题。
核心拉取逻辑修改
你原有代码存在一个小bug:每页拉取24条数据,但总页数计算时按25条/页做除法,会导致尾页数据异常,统一按24条/页计算即可。修改后的完整拉取函数如下:
const PAGE_SIZE = 24; const fetchPokemon = async () => { try { setLoading(true); const selectType = selectedType.toLowerCase(); const { start: regionStart, end: regionEnd } = REGION_ID_RANGE[selectedRegion]; const regionTotal = regionEnd - regionStart + 1; if (selectType === "all") { // 计算当前页在地区区间内的偏移量和拉取数量,避免跨地区拉取 const offset = regionStart - 1 + PAGE_SIZE * page; // 偏移量超过地区最大ID时直接返回空 if (offset >= regionEnd) { setPokemon([]); setTotal(0); setNotFound(true); setLoading(false); return; } // 最后一页不足24条时自动截断limit const limit = Math.min(PAGE_SIZE, regionEnd - offset); const data = await getPokemon(limit, offset); const promises = data.results.map(async (pokemon) => { return await getPokemonData(pokemon.url); }); const results = await Promise.all(promises); setPokemon(results); setTotal(Math.ceil(regionTotal / PAGE_SIZE)); setNotFound(results.length === 0); } else { // 类型筛选分支:先拉取全量该类型宝可梦,再按地区ID过滤 const { data: typePokemonList } = await searchPokemonByType( selectType, 9999, // 拉取该类型全量数据,type接口单类型返回数据量峰值不超过200条,无性能压力 0 ); // 过滤出当前地区区间内的宝可梦,从url中提取宝可梦ID做判断 const regionFilteredList = typePokemonList.filter(item => { const pokemonId = Number(item.pokemon.url.split('/').filter(Boolean).pop()); return pokemonId >= regionStart && pokemonId <= regionEnd; }); // 对过滤后的数据做前端分页 const pageStart = PAGE_SIZE * page; const pageData = regionFilteredList.slice(pageStart, pageStart + PAGE_SIZE); const promises = pageData.map(async (pokemon) => { return await getPokemonData(pokemon.pokemon.url); }); const results = await Promise.all(promises); setPokemon(results); setTotal(Math.ceil(regionFilteredList.length / PAGE_SIZE)); setNotFound(results.length === 0); } setLoading(false); } catch (err) { setLoading(false); setNotFound(true); } };
可选优化方案
如果不想硬编码ID区间,可以在应用初始化时调用PokeAPI的世代接口拉取每个世代对应的宝可梦列表,提取ID后动态生成REGION_ID_RANGE映射表,后续官方更新世代时不需要手动修改硬编码值。但对于个人项目来说,硬编码的方式性能更好,不需要额外的首屏请求,完全够用。
内容的提问来源于stack exchange,提问作者Wasivis
相关产品推荐
相关产品推荐

