I am doing a memory game in which every time the user clicks on a card it executes flipcard passing the card id to it, and changes the flip status of that card, updating the gameState.
After a card is flipped, I want to execute checkMatchingCards to check if the flipped cards are of the same type, if they aren't it must flip the cards back, updating gameState, BUT after a second, not instantly.
What is the best way to assure that both functions are executed in that order, flipcard and then checkMatchingCards with the last one waiting a second?
const flipCard = (id) => {
if (!canFlipCard(id)) return
// Find card to flip
const cardToFlip = gameState.cards.find(card => card.id === id)
cardToFlip.isFlipped = !cardToFlip.isFlipped
setGameState({
...gameState,
cards: gameState.cards.map(card => (card.id === cardToFlip.id ? cardToFlip : card)),
flippedCards: gameState.flippedCards.concat(cardToFlip)
})
}
const checkMatchingCards = (cards) => {
if (!cards) return
const cardsMatch = cards.every(card => card.color === cards[0].color)
// If cards match and reach number of cards to match
if (cardsMatch && cards.length === MATCH_GOAL) {
setGameState({
...gameState,
flippedCards: [],
matchedCards: gameState.matchedCards.concat(cards),
})
}
// If cards doesn't match, clean flippedCards
else if (!cardsMatch && cards.length >= 2) {
setGameState({
...gameState,
cards: gameState.cards.map(card => gameState.flippedCards.includes(card, 0) ? { ...card, isFlipped: false } : card),
flippedCards: [],
})
}
}
I found two ways of doing this, one is using setTimeout so every time the game renders, it checks for matching cards,
setTimeout(() => {
checkMatchingCards(gameState.flippedCards)
}, 900)
And another way with useEffect and setTImeout, which executes only when flippedCards change:
useEffect(()=> {
setTimeout(() => {
checkMatchingCards(gameState.flippedCards)
}, 900)
}, gameState.flippedCards)
I leave the entire code here, in case it helps https://github.com/takihama/memory-game
You should use useEffect
in this case.
Don't call setTimeout
outside of useEffect
, this will be called every time the component re-render (which isn't necessary since checkMatchingCards
only depends on gameState.flippedCards
).
And you should passuseEffect
dependencies as an array
useEffect(()=> {
setTimeout(() => {
checkMatchingCards(gameState.flippedCards)
}, 900)
}, [gameState.flippedCards])