Home > Enterprise >  React Firebase onSnapshot returning null for mapping function
React Firebase onSnapshot returning null for mapping function

Time:07-19

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?

CodePudding user response:

This is one source of problems:

if (activeFilter === "") {
  setFiltered("");
  return;
}

You're setting the filtered state variable to an empty string, but all other code (including when you declare filtered in useState assumes that it is an array.

My guess is that you want to do:

if (activeFilter === "") {
  setFiltered([]);
  return;
}
  • Related