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
相关产品推荐
相关产品推荐

