1

I'm using useEffect to fetch some data from Trello and set some states. First I grab the card I'm looking for and call setCard and setCardLocation. Everything is working fine. Then I get into my else case and no matter what I do setPublishDate will never be set, the loop continues to run. Why do all of these other hooks work but my last one doesn't? Thanks.

export default function Home(props) { const [performedFetch, setPerformedFetch] = useState(false); const [slug, setSlug] = useState(null); const [cardLocation, setCardLocation] = useState(1); const [card, setCard] = useState(null); const [publishDate, setPublishDate] = useState(null); const key = ''; // imagine these are here const token = ''; useEffect(() => { setSlug( new URLSearchParams(window.location.search).get('slug') ); if (!performedFetch && !!slug) { fetch(`https://api.trello.com/1/lists/${listId}/cards?key=${key}&token=${token}`) .then(response => response.json()) .then(data => { setPerformedFetch(true); data.forEach((c, index) => { if (c.desc.includes(slug)) { setCard(c) setCardLocation(index + 1) } else if (!publishDate && index > cardLocation) { console.log(publishDate); // why is this always null?? also runs multiple times const name = c.name; const frontHalf = name.split("/")[0].split(" "); const month = frontHalf[frontHalf.length - 1]; const day = name.split("/")[1].split(")")[0]; setPublishDate(`${month}/${day}`); } }); }); } }); 
2
  • I think this is related to this: reactjs.org/docs/… Commented May 16, 2020 at 20:24
  • 1
    Two useEffect() as @TaghiKhavari says, or just use slug locally within the useEffect - const slug = new URLSearchParams(window.location.search).get('slug'). Commented May 16, 2020 at 21:04

2 Answers 2

1

As already mentioned by @TaghiKhavari, you should have two useEffects (Multiple effects to separate concerns).

Also, it is important to optimize the performance by skipping effects by providing a dependency array as second argument to the useEffect. So the effect will only re-run if any of its dependencies would change.

First effect for slug:

useEffect(() => { setSlug( new URLSearchParams(window.location.search).get('slug') ); }, []) // Note: Remove "[]" if you want to set slug at each update / render Or keep it if you want to set it only once (at mount) 

Second effect to fetch and set card and other details:

useEffect(() => { if (!performedFetch && slug) { fetch( `https://api.trello.com/1/lists/${listId}/cards?key=${key}&token=${token}` ) .then((response) => response.json()) .then((data) => { setPerformedFetch(true) // Note: if there can be only ONE matching card const index = data.findIndex((card) => card.desc.includes(slug)) if (index > -1) { const card = data[index] setCard(card) setCardLocation(index + 1) const name = card.name const frontHalf = name.split('/')[0].split(' ') const month = frontHalf[frontHalf.length - 1] const day = name.split('/')[1].split(')')[0] setPublishDate(`${month}/${day}`) } // Setting State in a LOOP? is a problem /* data.forEach((card, index) => { if (card.desc.includes(slug)) { setCard(card) setCardLocation(index + 1) } else if (!publishDate && index > cardLocation) { const name = card.name const frontHalf = name.split('/')[0].split(' ') const month = frontHalf[frontHalf.length - 1] const day = name.split('/')[1].split(')')[0] setPublishDate(`${month}/${day}`) } })*/ }) } }, [slug, performedFetch]) 

Set states may be async to improve performance:

So, you should not set states in a loop as you are doing currently. If you must iterate through a loop and set all or few elements of the array in state, you can loop through the array and push all relevant items in a local array variable and set it to state after loop ends. Hope it helps!

Sign up to request clarification or add additional context in comments.

Comments

1

It's because usually react states updates asynchronously and at the time you're checking for slug it hasn't set yet

you need to do something like this:

function Home(props) { const [performedFetch, setPerformedFetch] = useState(false); const [slug, setSlug] = useState(null); const [cardLocation, setCardLocation] = useState(1); const [card, setCard] = useState(null); const [publishDate, setPublishDate] = useState(null); const key = ""; // imagine these are here const token = ""; useEffect(() => { setSlug(new URLSearchParams(window.location.search).get("slug")); }); useEffect(() => { console.log(slug) if (!performedFetch && !!slug) { fetch(`https://api.trello.com/1/lists/${listId}/cards?key=${key}&token=${token}`) .then(response => response.json()) .then(data => { setPerformedFetch(true); data.forEach((c, index) => { if (c.desc.includes(slug)) { setCard(c) setCardLocation(index + 1) } else if (!publishDate && index > cardLocation) { console.log(publishDate); // why is this always null?? also runs multiple times const name = c.name; const frontHalf = name.split("/")[0].split(" "); const month = frontHalf[frontHalf.length - 1]; const day = name.split("/")[1].split(")")[0]; setPublishDate(`${month}/${day}`); } }); }); } }, [slug, performedFetch]) } 

3 Comments

Sorry but this isn't correct. My code runs fine and the slug is pulled from the URL bar so the slug has nothing to do with anything async. I get into the else block and it continues to run through the cards I fetched. I'd expect it to stop after the first time I call setPublishedDate. Logging immediately after I set it still returns null.
@ZackShapiro , It's because React will batch all of your setPublishDate calls and will update state after you're checking the publish date
So how can I fix? Thanks

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.