Search code examples
javascriptreactjssettimeoutrendersetstate

In React, why does setState in a setTimeout only work properly with a copy of the state?


In my code below, I placed setCards inside a setTimeout but after 1 second the render will only be correct if I pass in a copy of the state: [...cardStates] rather than cardStates itself. If I don't do this, the setCards will render the cardStates as it was before hideCards() made changes to the state.

I think this has something to do with the state when setTimeout places the function in the queue but I don't fully grasp what's happening. I've dug around a lot and things like stale closures came up but I'm not sure if this is the correct direction. Can someone help clear this up for me? Thank you.

useEffect(() => {
        if (numberShowing(cardStates) === 2) {
            setTimeout(() => {
                setCards(hideCards([...cardStates]));
            }, 1000)
        }
    })

function hideCards(cards) {
    let showing = cards.filter(card => {
        return card.cardState === 'showing';
    })
    showing.forEach(card => {
        card.cardState = 'hiding';
    });
    return cards;
}

Solution

  • Because a state is immutable, you can't manipulate it directly. Try to debug something with mutable data, in some case it can become painful.

    That's not the only benefit to immutability but i won't do better explanation than you can already find on internet ;).