This component with map method is giving me an error that says Rendered fewer hooks than expected, only when I am filtering, i don’t quite get, any help would much appreciated.
I know usually, The error "Rendered fewer hooks than expected" in a React component typically occurs when the order of hooks or the number of hooks called varies between renders. In my code, the filtering the event is based on the country value and nothing else.
useEffect(() => {
setfilteredEvents(
events.filter((event) => event.country === selectedCountry)
);
}, [country]);
even when i do something like this manually is still causing the problem
useEffect(() => {
setfilteredEvents(
events.filter((event) => event.country === "France")
);
}, [country]);
Here is the full code
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";
export default function EventsList({ expanded, setExpanded }: EventsListProps) {
const { country } = eventStore();
const selectedCountry = country;
const [filteredEvents, setfilteredEvents] = useState(events);
useEffect(() => {
setfilteredEvents(
events.filter((event) => event.country === selectedCountry)
);
// console.log(filteredEvents);
}, [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) => {
const [imageLoaded, setImageLoaded] = useState(false);
const onLoad = () => {
setImageLoaded(true);
};
return (
<div
key={event.id}
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>
);
})}
</div>
</div>
);
}
type EventsListProps = {
expanded: boolean;
setExpanded: CustomDispatch<boolean>;
};
>Solution :
React tracks state across function component renders, so when you conditionally use a hook (like useState) it causes problems for react. "Conditionally", in your case, means that across renders it will likely be rendered a different number of times.
Instead of doing this nested in the parent component, create a child component as shown below.
The reason this works is that the child component result is memoized and not re-evaluated. In your code, useState is called an additional time for each item for each render.
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";
const EventItem = ({event}: { event: EventsType }) => {
const [imageLoaded, setImageLoaded] = useState(false);
const onLoad = () => {
setImageLoaded(true);
};
return (
<div
key={event.id}
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>
);
};
export default function EventsList({ expanded, setExpanded }: EventsListProps) {
const { country } = eventStore();
const selectedCountry = country;
const [filteredEvents, setfilteredEvents] = useState(events);
useEffect(() => {
setfilteredEvents(
events.filter((event) => event.country === selectedCountry)
);
// console.log(filteredEvents);
}, [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) => <EventItem event={event}/>) }
</div>
</div>
);
}
type EventsListProps = {
expanded: boolean;
setExpanded: CustomDispatch<boolean>;
};