如何阻止筛选后的React组件反复重渲染?
如何阻止筛选后的React组件反复重渲染?
我一眼就揪出问题的根源了——你的HikeCard组件里藏着一个“破坏分子”,直接导致状态被意外覆盖,才会出现「先显示收藏列表,又跳回全量列表」的诡异情况!
核心问题拆解
你在HikeCard里写了一个空依赖的useEffect,每次这个卡片组件挂载时,它都会偷偷发起请求拉取全部徒步数据,然后调用setCards把所有数据塞进去。这直接就把你在FavoriteHikes里好不容易筛选出来的「仅收藏数据」给冲没了!
走一遍流程你就懂了:
FavoriteHikes挂载 → 执行useEffect拉取数据、筛选出收藏项 →cards变成收藏列表,页面显示收藏的徒步卡片List组件渲染对应的HikeCard→ 每个HikeCard挂载时触发自身的useEffect,拉取全量数据并调用setCards→cards被改回全量列表- 页面跟着从收藏列表跳回全量列表,这就是你看到的现象
一步步解决问题
1. 立刻移除HikeCard里的非法数据逻辑
HikeCard的职责应该只是展示单张卡片+触发操作,根本不需要自己去拉取全量数据。把这个组件里的useEffect和asyncFunction全部删掉:
import { HikeCardProps } from "../../Type"; // 对应你的类型定义 export default function HikeCard({ card, deleteCard, updateCard }: HikeCardProps) { // 删掉原来的useEffect和fetch逻辑!!! return ( <div id="hike-card"> <div className="heart-icon" onClick={() => updateCard(card.id, { favorite: !card.favorite })}> <div></div> </div> <div id="hike-card-2" className="card width: 18rem"> <div id="image-div"> <img id="card-image" src={card.imageUrl} className="card-img-top" alt={`${card.name} ${card.location} ${card.miles}`}/> </div> <div className="card-body"> <h5 className="card-title">{card.name}</h5> <h6 id="card-location">{card.location}</h6> <h6 id="card-miles">{card.miles} miles</h6> <button onClick={() => deleteCard(card.id)} className="button m-2 btn btn-outline-danger" style={{width: "30%"}}>Delete Hike</button> <button onClick={() => updateCard(card.id, { favorite: !card.favorite })} className="button m-2 btn btn-outline-primary" style={{width: "30%"}}>Favorite Hike</button> </div> </div> </div> ) }
2. 修正UpdateCard里的小笔误
你的updateCard函数里有个低级错误,会导致更新后的数据状态异常:
// 原来的错误代码:把整个cards数组展开了,完全逻辑错误 { ...cards, ...data } // 应该改成展开单个card对象,合并新数据 { ...card, ...data }
修改后的updateCard函数:
const updateCard = async (id: string, updatedCard: Partial<Hike>) => { try { const response = await fetch(`${API_URL}/${id}`, { method: "PUT", headers: { 'content-type':'application/json' }, body: JSON.stringify(updatedCard), }); if (!response.ok) { throw new Error("Network response was not ok."); } const data = await response.json(); setCards( cards.map(card => ( card.id !== id ? card : { ...card, ...data } // 这里修正为单个card )) ); } catch (error) { console.error("There was an error when updating a card:", error); throw error; } };
3. 优化状态管理(可选)
如果你的应用里有多个地方需要用到徒步数据(比如普通列表和收藏列表),建议把数据获取逻辑提到更上层的组件(比如App.tsx),用Props或者Context把数据和操作函数传递给子组件。这样既能避免重复发起请求,还能统一管理状态,不会再出现子组件乱改父状态的情况。
关于严格模式的说明
你提到没禁用严格模式,其实开发环境的严格模式会双重执行useEffect等函数,但这不是你问题的根源——就算关了严格模式,HikeCard里的那个useEffect还是会在挂载时覆盖你的状态。所以不用为了这个问题关严格模式,它能帮你提前发现很多潜在的代码问题。
内容来源于stack exchange
相关产品推荐
相关产品推荐

