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

React框架卡片搜索过滤返回空数组问题排查

React搜索过滤返回空数组问题排查
  • 开发场景:基于React开发鸟类网页搜索引擎,在Searchcard.js中通过三元运算实现搜索过滤逻辑
  • 预期逻辑:搜索状态search为空时展示全量卡片数据,search有输入值时按输入内容匹配过滤对应数据
  • 异常现象:无输入时全量展示正常,输入搜索内容后页面无数据,存储过滤结果的res变量始终返回空数组

初始错误过滤代码

const res = !search ? data : data.filter((dato) => dato.toString().toLowerCase().includes(search.toLocaleLowerCase()))

这段代码的问题是直接对数组项dato(对象类型)调用toString(),返回的是固定字符串[object Object],自然无法匹配搜索关键词。

第一次调整后的代码

const res = !search ? data : data.filter((dato) => dato.name.toString().toLowerCase().includes(search.toLocaleLowerCase()))

调整后依然返回空数组,调试确认API返回的原始data数据正常,但过滤结果始终为空。


相关代码文件

Searchcard.js(搜索页主组件)

import React from 'react'
import { useState } from 'react'
import {Cards} from '../cards/Cards'
import { UseFetch } from '../UseFetch'

export const Searchcard = () => {
    //hooks
    const [url, setUrl] = useState("https://aves.ninjas.cl/api/birds") 
    const estado = UseFetch(url) /* 获取UseFetch返回的状态对象(加载状态、数据) */
    const {cargando, data} = estado /* 从返回对象中解构加载状态和接口数据 */
    const [search, setSearch] = useState("")
    
    // 捕获用户输入的搜索值
    const searcher = (e) => {
        setSearch(e.target.value)
        console.log(e.target)
    }
    // 数据过滤逻辑
    const res = !search ? data : data.filter((dato) => dato.name.toString().toLowerCase().includes(search.toLocaleLowerCase()))
    
  return (
    <div>
        <input value={search} onChange={searcher} type="text" placeholder='¡Busca un pajaro!' className='form-control'></input>
    
    {
          cargando 
          ?
          <h1>Cargando...</h1>
           :
           <div>
            <Cards data={res} />    
            {console.log(data)}  
            {console.log(res)}
          </div>
  }
    
    </div>
  )
}

export default Searchcard

UseFetch.js(自定义数据请求Hook)

import { useState, useEffect } from 'react'
export const UseFetch = (url) => {
  const [resultado, setResultado] = useState({ cargando: true, dato: null })
  useEffect( ()=>{
    getData(url)
  },[url])
  async function getData(url) {
    try{
      setResultado({cargando: true, data:null})
      const respues = await fetch(url)       
      const data = await respues.json()       
      setResultado({ cargando: false, data })
    }
    catch(e){
      console.log(e)
    }
  }
  return resultado
}

cards.js(卡片列表组件)

import React, {useState} from 'react'
import {Cardsbird} from './Cardsbird'
import './Cards.css'
import Paginacion from '../Pagination/Pagination'

export const Cards = ({data}) => {
  const [actPag, setactPag] = useState(1)
  const [cardXpag] = useState(20)

  // 分页切片逻辑
  const indUltimo = actPag * cardXpag;
  const indPrimero = indUltimo - cardXpag;
  const actCard = data.slice(indPrimero, indUltimo); 
  
  // 切换页码
  const paginate = numPag => setactPag(numPag);

  return (
    <div className='container'>
      
        <ul className='cards'>
            {actCard.map(p=>(
                  <li className='card-item' key={p.uid}>
                      <Cardsbird url={p._links.self}/>
                  </li>  
                ))
            }
            <div className='container2'><Paginacion cardXpag={cardXpag} totalCards={data.length} paginate={paginate}/></div>
        </ul>
    </div>
  )
}

export default Cards

cardsbird.js(单卡片渲染组件)

import React from 'react'
import { UseFetch } from '../UseFetch'
import Modal from './Modal'

export const Cardsbird = ({url}) => {
    const estado = UseFetch(url)
    const {cargando, data} = estado 

  return (
    <div>
        {
            cargando
            ?
            <h1>Cargando...</h1> :
            <div className='card' style={{width:'14rem'}} data-bs-toggle="modal" data-bs-target={`#id${data.sort}`}>
                <div className='card-header'>
                    <h5 className='card-title'>{data.name.spanish}</h5>
                </div>
                <div className='card-body'>
                    <img src={data.images.main} alt={data.uid} />
                </div>
                <Modal id={`id${data.sort}`} titulo={data.name.spanish} imagen={data.images.main} audio={data.audio.file} descripcion={data.iucn.description} habitat={data.habitat} tamaño={data.size} mapa={data.map.image} />           
            </div>
        }
    </div>
  )
}

export default Cardsbird

问题根因

从cardsbird.js的渲染逻辑可以看出,接口返回的列表项中name字段不是字符串,而是一个多语言对象,结构为name: { spanish: "鸟类西语名称", english: "鸟类英语名称", ... }。
过滤时直接对dato.name这个对象调用toString(),得到的结果是固定字符串[object Object],永远不可能匹配用户输入的搜索关键词,因此过滤结果始终为空数组。

修复方案

修改过滤逻辑,指定要匹配的name下的具体文本字段,比如匹配西语名称:

const res = !search 
  ? data 
  : data.filter((dato) => 
      dato.name.spanish.toLowerCase().includes(search.toLocaleLowerCase())
    )

如果需要同时匹配多语言名称,可以扩展判断逻辑:

const res = !search 
  ? data 
  : data.filter((dato) => {
      const keyword = search.toLocaleLowerCase()
      return dato.name.spanish.toLowerCase().includes(keyword) 
        || dato.name.english.toLowerCase().includes(keyword)
        // 可继续添加其他需要匹配的字段
    })

额外优化点:Cards组件中分页状态actPag默认值为1,建议在搜索关键词search变化时重置actPag为1,避免出现搜索后当前页码无数据的问题。

内容的提问来源于stack exchange,提问作者Ffuentes

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:12:30