React基于DogAPI的狗狗列表筛选方案咨询(搜索框/按钮)
给React狗狗列表页添加筛选功能(最小改动方案)
下面给出两种筛选方案,均无需大幅修改现有代码,且完全不影响通过ID跳转详情页的功能——因为我们只是在渲染时对原数据做过滤,原始数据的ID等信息完全保留。
方案一:搜索框筛选
改动点
- 添加
searchTerm状态存储搜索关键词 - 新增搜索输入框,绑定
searchTerm - 渲染列表时,根据搜索词过滤狗狗数据(匹配名称、用途等字段)
修改后的完整代码
import { useState, useEffect } from "react"; import { Link } from 'react-router-dom'; import Loading from "./Loading"; export default function App() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // 新增搜索词状态 const [searchTerm, setSearchTerm] = useState(""); useEffect(() => { fetch(`https://api.thedogapi.com/v1/breeds`, { method: "GET", headers: { 'Content-Type': 'application/json', 'x-api-key': `${process.env.REACT_APP_API_KEY}`, }, }) .then((response) => { if (!response.ok) { throw new Error(`This is an HTTP error: The status is ${response.status}`); } return response.json(); }) .then((actualData) => { setData(actualData); setError(null); }) .catch((err) => { setError(err.message); setData(null); }) .finally(() => { setLoading(false); }); }, []); // 新增过滤逻辑 const filteredDogs = data?.filter(dog => dog.name.toLowerCase().includes(searchTerm.toLowerCase()) || dog.bred_for?.toLowerCase().includes(searchTerm.toLowerCase()) ) || []; return <div className="container-fluid"> {/* 新增搜索框 */} <div className="row justify-content-center my-4"> <input type="text" placeholder="搜索狗狗名称/用途..." className="col-md-6 form-control" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} /> </div> {loading && <div className="text-center"><Loading /></div>} {error && ( <div>{`获取数据失败 - ${error}`}</div> )} <ul className="row justify-content-center"> {filteredDogs.map(({id,name,image,bred_for,life_span}) => ( <Link to={`/dog/${id}`} className="col-md-4 col-sm-6 card my-3 py-3 border-0"> <li key={id}> <h3>{name}</h3> <div className="card-img-top text-center"> <img src={image.url} className="photo w-75" /> </div> <p>{bred_for}</p> <p>{life_span}</p> </li> </Link> ))} </ul> </div>; }
说明
- 搜索逻辑同时匹配狗狗名称(
name)和用途(bred_for),不区分大小写 - 过滤后的
filteredDogs保留了原数据的所有字段(包括id),因此跳转详情页的Link完全不受影响 - 仅新增了3处代码:状态声明、过滤函数、搜索框UI,原数据获取和列表渲染逻辑几乎没改
方案二:按犬种分类按钮筛选
改动点
- 添加
selectedBreedGroup状态存储选中的分类 - 从原数据中提取所有唯一的犬种分类(基于
breed_group字段) - 渲染分类按钮组,点击时切换选中的分类
- 根据选中的分类过滤狗狗数据
修改后的完整代码
import { useState, useEffect } from "react"; import { Link } from 'react-router-dom'; import Loading from "./Loading"; export default function App() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); // 新增选中分类状态 const [selectedBreedGroup, setSelectedBreedGroup] = useState("all"); useEffect(() => { fetch(`https://api.thedogapi.com/v1/breeds`, { method: "GET", headers: { 'Content-Type': 'application/json', 'x-api-key': `${process.env.REACT_APP_API_KEY}`, }, }) .then((response) => { if (!response.ok) { throw new Error(`This is an HTTP error: The status is ${response.status}`); } return response.json(); }) .then((actualData) => { setData(actualData); setError(null); }) .catch((err) => { setError(err.message); setData(null); }) .finally(() => { setLoading(false); }); }, []); // 提取所有唯一的犬种分类 const breedGroups = data ? [...new Set(data.map(dog => dog.breed_group).filter(Boolean))] : []; // 新增分类过滤逻辑 const filteredDogs = data?.filter(dog => selectedBreedGroup === "all" || dog.breed_group === selectedBreedGroup ) || []; return <div className="container-fluid"> {/* 新增分类按钮组 */} <div className="row justify-content-center my-4 flex-wrap"> <button className={`btn mx-1 my-1 ${selectedBreedGroup === "all" ? "btn-primary" : "btn-outline-primary"}`} onClick={() => setSelectedBreedGroup("all")} > 全部 </button> {breedGroups.map(group => ( <button key={group} className={`btn mx-1 my-1 ${selectedBreedGroup === group ? "btn-primary" : "btn-outline-primary"}`} onClick={() => setSelectedBreedGroup(group)} > {group} </button> ))} </div> {loading && <div className="text-center"><Loading /></div>} {error && ( <div>{`获取数据失败 - ${error}`}</div> )} <ul className="row justify-content-center"> {filteredDogs.map(({id,name,image,bred_for,life_span}) => ( <Link to={`/dog/${id}`} className="col-md-4 col-sm-6 card my-3 py-3 border-0"> <li key={id}> <h3>{name}</h3> <div className="card-img-top text-center"> <img src={image.url} className="photo w-75" /> </div> <p>{bred_for}</p> <p>{life_span}</p> </li> </Link> ))} </ul> </div>; }
说明
- 基于DogAPI返回的
breed_group字段(如"猎犬"、"梗犬"等)生成分类按钮 - 点击"全部"按钮可恢复显示所有狗狗
- 过滤后的
filteredDogs保留原数据的id字段,跳转详情页功能完全正常 - 仅新增了状态声明、分类提取、过滤函数和按钮组UI,原核心逻辑未改动
通用注意事项
两种方案都是在客户端对已获取的全量数据进行过滤,无需重新请求API,性能开销极小;且完全保留了原数据的所有字段,因此通过id跳转详情页的功能不会受到任何影响。
内容的提问来源于stack exchange,提问作者Jan Peeter
相关产品推荐
相关产品推荐

