React中useEffect过滤数组的错误用法及修复方法
解决React筛选后无法恢复全部卡片的问题
嘿,这个问题我之前踩过坑!核心原因很明确——你把原始的数据源直接存在了cards状态里,每次筛选都会覆盖这个状态,导致原始数据丢失,自然没法回到显示全部卡片的状态啦。
问题出在哪?
看你的代码,useEffect每次searchField变化时,会用当前的cards状态去筛选:
const filteredCards = cards.filter(...) setCards(filteredCards);
第一次筛选后,cards已经变成了过滤后的小数据集。当你清空搜索框时,是用这个小数据集去筛选包含空字符串的结果,结果当然还是这个小数据集,没法恢复到最初的全部数据。
修复方案:保留原始数据源
我们需要把原始数据和过滤后的结果分开,永远基于原始数据做筛选操作,而不是修改原始数据的状态。这里有几种实现方式:
方式1:直接在渲染时计算过滤结果(最简洁)
最简单的方式是去掉useEffect,直接在组件渲染时基于原始data计算过滤结果,逻辑更清晰:
import { data } from './data'; import { useState } from 'react'; const SearchBox = ({ onSearchChange }) => { return ( <div> <input type='search' placeholder='search' onChange={(e) => { onSearchChange(e.target.value); }} /> </div> ); }; function App() { const [searchField, setSearchField] = useState(''); // 每次渲染时基于原始data计算过滤结果 const filteredCards = data.filter((card) => { return card.name.toLowerCase().includes(searchField.toLowerCase()); }); return ( <div> <SearchBox onSearchChange={setSearchField} /> <CardList cards={filteredCards} /> </div> ); }
方式2:用useMemo优化性能(适用于大数据量)
如果你的data很大,每次渲染都重新过滤可能影响性能,可以用useMemo缓存过滤结果,只有当searchField或data变化时才重新计算:
import { data } from './data'; import { useState, useMemo } from 'react'; const SearchBox = ({ onSearchChange }) => { return ( <div> <input type='search' placeholder='search' onChange={(e) => { onSearchChange(e.target.value); }} /> </div> ); }; function App() { const [searchField, setSearchField] = useState(''); // 缓存过滤结果,避免不必要的重复计算 const filteredCards = useMemo(() => { return data.filter((card) => { return card.name.toLowerCase().includes(searchField.toLowerCase()); }); }, [searchField, data]); // 依赖项:只有这两个值变了才重新计算 return ( <div> <SearchBox onSearchChange={setSearchField} /> <CardList cards={filteredCards} /> </div> ); }
方式3:适配动态获取的原始数据(比如接口请求)
如果你的data不是静态导入的,而是从接口动态获取的,可以把原始数据单独存在状态里,再基于它做筛选:
import { useState, useEffect, useMemo } from 'react'; function App() { const [originalCards, setOriginalCards] = useState([]); const [searchField, setSearchField] = useState(''); // 模拟从接口获取原始数据 useEffect(() => { fetch('/api/cards') .then(res => res.json()) .then(data => setOriginalCards(data)); }, []); const filteredCards = useMemo(() => { return originalCards.filter((card) => { return card.name.toLowerCase().includes(searchField.toLowerCase()); }); }, [searchField, originalCards]); return ( <div> <SearchBox onSearchChange={setSearchField} /> <CardList cards={filteredCards} /> </div> ); }
核心总结
不管用哪种方式,关键都是不要覆盖原始数据源,所有筛选操作都要基于原始数据来执行。这样当你清空搜索框时,过滤逻辑会基于全部原始数据重新计算,自然就能回到显示全部卡片的状态啦。
内容的提问来源于stack exchange,提问作者sara lance
相关产品推荐
相关产品推荐

