You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React基于DogAPI的狗狗列表筛选方案咨询(搜索框/按钮)

给React狗狗列表页添加筛选功能(最小改动方案)

下面给出两种筛选方案,均无需大幅修改现有代码,且完全不影响通过ID跳转详情页的功能——因为我们只是在渲染时对原数据做过滤,原始数据的ID等信息完全保留。


方案一:搜索框筛选

改动点

  1. 添加searchTerm状态存储搜索关键词
  2. 新增搜索输入框,绑定searchTerm
  3. 渲染列表时,根据搜索词过滤狗狗数据(匹配名称、用途等字段)

修改后的完整代码

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,原数据获取和列表渲染逻辑几乎没改

方案二:按犬种分类按钮筛选

改动点

  1. 添加selectedBreedGroup状态存储选中的分类
  2. 从原数据中提取所有唯一的犬种分类(基于breed_group字段)
  3. 渲染分类按钮组,点击时切换选中的分类
  4. 根据选中的分类过滤狗狗数据

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 06:45:33