I seem to be getting the error "filtered.map is not a function" and I’m assuming its because of my onSnapshot returning a null because it hasn’t fired yet to pull the data, (aka make it available) to be used to filter inside my component.
If I change the .map from filtered useState to my useState knives then I can get the data and no error and when I change it back I can then use my filter component without any issues.
Parent Component:
function KnifesComponent() {
const knifeCollection = collection(db, "knives");
const [knives, setKnives] = useState([]);
const [count, setCount] = useState(0);
const [filtered, setFiltered] = useState([]);
const [activeFilter, setActiveFilter] = useState("");
useEffect(() => {
const sub = onSnapshot(knifeCollection, (snapshot) => {
setKnives(snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
setFiltered(snapshot.docs.map((doc) => ({ ...doc.data(), id: doc.id })));
const TotalSkins = snapshot.size;
setCount(TotalSkins);
});
return () => {
sub();
}
}, []);
const rarities = {
Exclusive: "Rarity_Exclusive.png",
Ultra: "Rarity_Ultra.png",
Premium: "Rarity_Premium.png",
Deluxe: "Rarity_Deluxe.png",
};
return (
<div className="collection">
<h2 className="section-heading">
Knives <div className="count">({count})</div>
<Filter
skins={knives}
setFiltered={setFiltered}
activeFilter={activeFilter}
setActiveFilter={setActiveFilter}
/>
</h2>
{filtered.map((skin) => {
return (
<div
layout
animate={{ opacity: 1 }}
initial={{ opacity: 0 }}
exit={{ opacity: 0 }}
className="skin-box"
key={skin.id}
>
<div className="skin-rarity">
<Image src={require(`../../public/${rarities[skin.rarity]}`)} />
</div>
<h4>{skin.name}</h4>
<div className="skin-value">
<p>{skin.value}</p>
</div>
</div>
);
})}
<div className="clearfix"></div>
</div>
);
}
Filter Component:
function Filter({ setActiveFilter, activeFilter, setFiltered, skins }) {
useEffect(() => {
if (activeFilter === "") {
setFiltered("");
return;
}
const filtered = skins.filter((skin) => skin.rarity.includes(activeFilter));
console.log(filtered)
setFiltered(filtered);
}, [activeFilter]);
return (
<div className="filter-container">
<button
className={activeFilter === [] ? "active" : ""}
onClick={() => setActiveFilter([])}
>
All
</button>
<button
className={activeFilter === "Ultra" ? "active ultra" : "ultra"}
onClick={() => setActiveFilter("Ultra")}
>
Ultra
</button>
<button
className={
activeFilter === "Exclusive" ? "active exclusive" : "exclusive"
}
onClick={() => setActiveFilter("Exclusive")}
>
Exclusive
</button>
<button
className={activeFilter === "Premium" ? "active premium" : "premium"}
onClick={() => setActiveFilter("Premium")}
>
Premium
</button>
<button
className={activeFilter === "Deluxe" ? "active deluxe" : "deluxe"}
onClick={() => setActiveFilter("Deluxe")}
>
Deluxe
</button>
</div>
);
}
What am I doing wrong or what do I need to change to always have the array filled for the filter to work?
2
Answers
Mistake was fixed I failed to see I need to update my setFiltered to an array instead of a empty string
to
This is one source of problems:
You’re setting the
filtered
state variable to an empty string, but all other code (including when you declarefiltered
inuseState
assumes that it is an array.My guess is that you want to do: