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

React组件过滤时出现Rendered fewer hooks than expected错误求助

解决React组件过滤时的“Rendered fewer hooks than expected”错误

问题出在你把useState放在了filteredEvents.map()的回调函数里,这直接违反了React Hook的核心规则:Hook必须在组件的顶层作用域调用,不能嵌套在循环、条件判断或者其他函数内部。

当执行过滤操作时,filteredEvents的元素数量会变化,导致每次渲染时调用的useState数量不一致(比如之前渲染10个元素对应10个Hook调用,过滤后只剩5个,Hook调用数就少了)。React依赖Hook调用的顺序和数量在每次渲染时保持一致来追踪状态,一旦这个一致性被打破,就会抛出“Rendered fewer hooks than expected”错误。

修复方案:抽离子组件

把map循环里的事件卡片逻辑抽成独立的EventCard子组件,将imageLoaded的状态放到子组件内部,这样每个子组件的Hook都在自身顶层调用,不会受父组件过滤操作的影响。

1. 创建EventCard子组件

import { useState } from "react";
import SecHeading from "../../../GlobalUI/SecHeading";
import EventsType from "../../../Types/EventsType";

export default function EventCard({ event }: { event: EventsType }) {
  const [imageLoaded, setImageLoaded] = useState(false);
  const onLoad = () => {
    setImageLoaded(true);
  };

  return (
    <div className="border border-1 border-black p-2 m-4 cursor-pointer rounded-lg max-w-[500px]">
      <div className="relative max-h-[500px]">
        <img
          src={event.photos[0]}
          alt=""
          className="hidden"
          onLoad={onLoad}
        />
        {imageLoaded ? (
          <img
            src={event.photos[0]}
            alt=""
            className="h-[250px] lg:h-[300px] w-full object-cover"
          />
        ) : (
          <div className="h-[250px] lg:h-[300px] animate-pulse w-full bg-gray-100"></div>
        )}
      </div>

      <div className="p-2 flex flex-col gap-2">
        <SecHeading css="">{event.title}</SecHeading>
        <p className="truncate max-w-[90%] text-gray-600">
          {event.description}
        </p>
      </div>
    </div>
  );
}

2. 修改原EventsList组件

在原组件中导入EventCard,并替换map循环里的逻辑:

import { useEffect, useState } from "react";
import SecHeading from "../../../GlobalUI/SecHeading";
import EventsType from "../../../Types/EventsType";
import ExpandedSearch from "./ExpandedSearch";
import TriggerSearch from "./TriggerSearch";
import eventStore from "../../../Store/eventStore";
import { events } from "../../../Data/eventsData";
import EventCard from "./EventCard"; // 导入子组件

export default function EventsList({ expanded, setExpanded }: EventsListProps) {
  const { country } = eventStore();
  const [filteredEvents, setfilteredEvents] = useState(events);

  useEffect(() => {
    // 直接用country,无需额外赋值selectedCountry
    setfilteredEvents(events.filter((event) => event.country === country));
  }, [country]);

  return (
    <div>
      <div
        className={
          expanded
            ? "mt-6  mb-2 open-searchbar "
            : "mt-6  mb-2 closed-searchbar"
        }
      >
        {expanded && <ExpandedSearch setExpanded={setExpanded} />}
      </div>
      <TriggerSearch expanded={expanded} setExpanded={setExpanded} />
      <div className="grid grid-cols-1 sm:grid-cols-2  justify-between sm:max-w-[90vw]">
        {/* 替换为子组件调用 */}
        {filteredEvents.map((event: EventsType) => (
          <EventCard key={event.id} event={event} />
        ))}
      </div>
    </div>
  );
}

type EventsListProps = {
  expanded: boolean;
  setExpanded: CustomDispatch<boolean>;
};

额外优化点

  • 去掉了冗余的selectedCountry变量,直接使用country即可;
  • 同步了useEffect的依赖数组,确保依赖准确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 17:27:43