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

如何阻止筛选后的React组件反复重渲染?

如何阻止筛选后的React组件反复重渲染?

我一眼就揪出问题的根源了——你的HikeCard组件里藏着一个“破坏分子”,直接导致状态被意外覆盖,才会出现「先显示收藏列表,又跳回全量列表」的诡异情况!

核心问题拆解

你在HikeCard里写了一个空依赖的useEffect,每次这个卡片组件挂载时,它都会偷偷发起请求拉取全部徒步数据,然后调用setCards把所有数据塞进去。这直接就把你在FavoriteHikes里好不容易筛选出来的「仅收藏数据」给冲没了!

走一遍流程你就懂了:

  1. FavoriteHikes挂载 → 执行useEffect拉取数据、筛选出收藏项 → cards变成收藏列表,页面显示收藏的徒步卡片
  2. List组件渲染对应的HikeCard → 每个HikeCard挂载时触发自身的useEffect,拉取全量数据并调用setCards → cards被改回全量列表
  3. 页面跟着从收藏列表跳回全量列表,这就是你看到的现象

一步步解决问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.08 14:38:12