0

This is going to be really hard to explain, but here goes. I am building a React card grid with a filter. The data is pulled from an MySQL AWS API I built. The .tags property is JSON with an array that stores each tag associated with the card. I have written Javascript in App.jsx to turn this JSON into an object, and then store every unique tag in a piece of state. See code below:

//App.jsx

import { useEffect, useState } from 'react';
import '../assets/css/App.css';
import Card from './Card';
import Filter from './Filter'
import {motion, AnimatePresence} from 'framer-motion'

function App() {

  const [cards, setCards] = useState([]);
  const [filter, setFilter] = useState([]);
  const [activeFilter, setActiveFilter] = useState("all");
  const [tags,setTags] = useState([]);

  useEffect(() => {
    fetchData();
  }, []);

  /*useEffect(() => {
    console.log(tags);
    console.log(activeFilter);
  }, [activeFilter,tags]);
*/
  const getTags = () => {
    let tags = [];
    cards.forEach((card) => {
      let obj = JSON.parse(card.tags);
      obj.forEach((tag) => {
        if (!tags.includes(tag)) {
          tags.push(tag);
        }
      });
    });
    setTags(tags);
  }

  const fetchData = async () => {
    const data = await fetch("<<api>>");
    const cards = await data.json();
    setCards(cards);
    setFilter((cards));
    getTags();
  }
 
  return (
    <div className="App">
      <Filter
      cards={cards}
      setFilter={setFilter}
      activeFilter={activeFilter}
      setActiveFilter={setActiveFilter}
      />
      <motion.div layout className="Cards">
        <AnimatePresence>
        {filter.map((card) => {
          return <Card key={card.id} card={card}/>;
        })}
        </AnimatePresence>
      </motion.div>
    </div>
  );
}

export default App;
 

The problem that I am having is that when I run the app initially, the tags state is empty when inspecting from React Dev tools. However, when I keep the app running, and then add something like a console.log(tags); before setTags(tags) is called in the getTags() function, the data suddenly appears in the state. If someone could explain why the state seems to be empty even though I am updating it on the initial render that would be really appreciated.

masonjacob
  • 36
  • 9

1 Answers1

0

You are running getTags on empty array. setCards doesn't set the const variable instantly. New values will be present in the next render cycle.

Try adding cards param

const getTags = (cards) => {
    let tags = [];
    cards.forEach((card) => {
      let obj = JSON.parse(card.tags);
      obj.forEach((tag) => {
        if (!tags.includes(tag)) {
          tags.push(tag);
        }
      });
    });
    setTags(tags);
  }

And use it like this:

const fetchData = async () => {
    const data = await fetch("API url");
    const cards = await data.json();
    setCards(cards);
    setFilter((cards));
    getTags(cards);
  }
Konrad
  • 21,590
  • 4
  • 28
  • 64
  • Thank you for replying so quickly. I added what you said, and now I am getting "App.jsx:30 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'forEach') at getTags (App.jsx:30:1) at fetchData (App.jsx:47:1)". Is this because cards still isn't initialized when I try to use the forEach mehod in getTags? – masonjacob Jul 19 '22 at 15:53
  • Are you sure you passed the `cards` argument and that your API returns anything? – Konrad Jul 19 '22 at 15:54
  • My API returns because on the initial render other API data renders correctly. My code now looks like this ```const getTags = (cards) => { let tags = []; cards.forEach((card) => { let obj = JSON.parse(card.tags); obj.forEach((tag) => { if (!tags.includes(tag)) { tags.push(tag); } }); }); setTags(tags); } const fetchData = async () => { const data = await fetch(<>); const cards = await data.json(); setCards(cards); setFilter((cards)); getTags(); } ``` – masonjacob Jul 19 '22 at 16:01
  • My bad, I forgot to add `cards` to `getTags(cards)` – Konrad Jul 19 '22 at 16:04
  • Ahh that worked, Thank you! For future reference, passing state as a parameter will make that function wait until the state has finished what its doing? Also, if you don't mind could you remove my api link from your comment I forgot to do that when I posted lol. – masonjacob Jul 19 '22 at 16:08
  • It won't wait, you just have these `cards` from the API not from the state in the `fetchData` function. – Konrad Jul 19 '22 at 16:09