This is a game that I am building and in this game I have created some levels. But when the game ends for the first time the second time my countdown timer is not getting decremented.
this is my App.js component:
import "./App.css";
import React, { useState, useEffect, useCallback } from "react";
import SingleCard from "./components/singleCard/SingleCard";
import Timer from "./components/timer/Timer";
import Modal from "./components/modal/Modal";
import soundOn from "./images/soundon.png";
import soundOff from "./images/soundoff.png";
import { Helmet } from "react-helmet";
const cardImages = [
{ src: "/img/img-1.png", matched: false },
{ src: "/img/img-2.png", matched: false },
{ src: "/img/img-3.png", matched: false },
{ src: "/img/img-4.png", matched: false },
{ src: "/img/img-5.png", matched: false },
{ src: "/img/img-6.png", matched: false },
];
function App({ background, wrongAns, correctAns, deadSound, winSound }) {
const [cards, setCards] = useState([]);
const [turns, setTurns] = useState(0);
const [choiceOne, setChoiceOne] = useState(null);
const [choiceTwo, setChoiceTwo] = useState(null);
const [disabled, setDisabled] = useState(false);
const [isgameEnd, setGameEnd] = useState(false);
const [timerStart, setTimerStart] = useState(false);
const [playSound, setPlaySound] = useState(false);
const [count, setCount] = useState(0);
const [IsPlaying, setIsPlaying] = useState(true);
const [isModalOpen, setModalIsOpen] = useState(false);
const [restartTimer, setRestartTimer] = useState(false);
const [isMute, setMute] = useState(false);
const [loading, setLoading] = useState(true);
function handleMute(state = false) {
background.muted = state;
wrongAns.muted = state;
correctAns.muted = state;
deadSound.muted = state;
winSound.muted = state;
setMute(state);
}
let timer;
// function that will decide the condition for opening the modal
const toggleModal = () => {
setModalIsOpen(true);
};
// function that will execute when we click a button in the modal
const handlePlaySound = () => {
setPlaySound(false);
};
// function that will execute when game is set to background in android
function AudioBgOnPause() {
if (playSound === true) {
background.pause();
setIsPlaying(false);
}
}
// functiona that will execute when game is again resumed
function AudioBgOnResume() {
if (IsPlaying === false) {
setIsPlaying(true);
}
}
// creating there global reference so that we can call these functions in the index file
window.AudioBgOnPause = AudioBgOnPause;
window.AudioBgOnResume = AudioBgOnResume;
// check if playSound is off or on
if (playSound === false) {
background.pause();
} else if (playSound === true && IsPlaying === true) {
background.play();
}
// Play Again
const playAgain = () => {
// setCards([]);
shuffleCards();
setTurns(0);
setChoiceOne(null);
setChoiceTwo(null);
setDisabled(false);
setGameEnd(false);
setTimerStart(false);
setPlaySound(false);
setCount(0);
setIsPlaying(true);
setModalIsOpen(false);
setRestartTimer(true);
setMute(false);
};
const restartGame = () => {
playAgain();
};
// check if isGameEnd is true i.e. the game is ended
// losing condition
useEffect(() => {
if (turns < 6 && isgameEnd === true) {
setDisabled(true);
setTimerStart(false);
clearInterval(timer);
if (playSound === true) {
deadSound.play();
}
setPlaySound(false);
setTimeout(() => {
toggleModal();
}, 2000);
}
}, [turns, isgameEnd]);
// winning situation
useEffect(() => {
if (
(turns === 6 && isgameEnd === false) ||
(turns === 6 && isgameEnd === true)
) {
// clearInterval(timer);
// setDisabled(true);
setRestartTimer(true);
setTimerStart(false);
if (playSound === true) {
winSound.play();
}
setPlaySound(playSound);
shuffleCards();
// setTimeout(() => {
// toggleModal();
// }, 2000);
}
}, [turns, isgameEnd]);
// shuffle Cards
const shuffleCards = () => {
const shuffleCards = [...cardImages, ...cardImages]
.sort(() => Math.random() - 0.5)
.map((card) => ({ ...card, id: Math.random() }));
setCards(shuffleCards);
setTurns(0);
};
// console.log("cards array", cards);
// handle a choice
const handleChoice = (card) => {
setTimerStart(true);
// background.play();
background.loop = true;
// checking if the counter is one only then set sound to true when the card is flipped for first time
count === 1 ? setPlaySound(true) : setPlaySound(playSound);
// after that increment the counter so that the upper condition should not hold anymore
setCount(count + 1);
choiceOne ? setChoiceTwo(card) : setChoiceOne(card);
};
// compare 2 selected cards
useEffect(() => {
if (choiceOne && choiceTwo) {
setDisabled(true);
if (choiceOne.src === choiceTwo.src) {
setCards((prevCards) => {
return prevCards.map((card) => {
if (card.src === choiceOne.src) {
return { ...card, matched: true };
} else {
return card;
}
});
});
if (playSound === true) {
correctAns.play();
}
setTurns((prevTurns) => prevTurns + 1);
resetTurn();
} else {
if (playSound === true) {
wrongAns.play();
}
setTimeout(() => resetTurn(), 500);
}
}
}, [choiceOne, choiceTwo]);
// start a new game automatically
// set counter to one when the component first mounts so that sound starts to play on first click only
useEffect(() => {
shuffleCards();
setCount(count + 1);
}, []);
// reset choices
const resetTurn = () => {
setChoiceOne(null);
setChoiceTwo(null);
setDisabled(false);
};
// console.log("restart App", restartTimer);
// timer callback
const onGameEnd = useCallback(() => {
setGameEnd(!isgameEnd);
}, [isgameEnd]);
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 4000);
}, []);
return (
<>
<Helmet>
<meta charSet="utf-8" />
<title>Match Maker</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
</Helmet>
<div className="App">
{loading && (
<div className="loader-container">
<div className="spinner"></div>
</div>
)}
<>
{/* <img
className="logo"
src="https://cheetay.pk/static/images/newLandingPage/logo.svg"
alt="card back"
/> */}
<div
style={
loading ? { visibility: "hidden" } : { visibility: "inherit" }
}
>
<div className="soundBtn">
{!isMute === true ? (
<div
className="soundIcon"
style={{
cursor: "pointer",
khtmlUserSelect: "none",
MozUserSelect: "none",
OUserSelect: "none",
userSelect: "none",
}}
onClick={() => handleMute(!isMute)}
>
<img src={soundOn} alt="soundOff" />
</div>
) : (
<div
className="soundIcon"
style={{
cursor: "pointer",
khtmlUserSelect: "none",
MozUserSelect: "none",
OUserSelect: "none",
userSelect: "none",
}}
onClick={() => handleMute(!isMute)}
>
<img src={soundOff} alt="soundOff" />
</div>
)}
</div>
<div className="card-grid">
{cards.map((card) => (
<SingleCard
key={card.id}
card={card}
handleChoice={handleChoice}
flipped={
card === choiceOne || card === choiceTwo || card.matched
}
disabled={disabled}
isModalOpen={isModalOpen}
/>
))}
</div>
<div className="TimerAndTurnsInfo">
<Timer
timerStart={timerStart}
timer={timer}
onGameEnd={onGameEnd}
restartTimer={restartTimer}
/>
<p>matched {turns}</p>
</div>
</div>
</>
</div>
{isModalOpen && (
<Modal handlePlaySound={handlePlaySound} restartGame={restartGame} />
)}
</>
);
}
export default App;
and this is my timer component:
import React, { useEffect, useState } from "react";
import "./Timer.css";
const Child = ({ timerStart, timer, onGameEnd, restartTimer }) => {
const [seconds, setSeconds] = useState(40);
// let time = 40;
useEffect(() => {
if (restartTimer === true) {
setSeconds(40);
}
}, [seconds, restartTimer]);
// console.log("restart Timer", restartTimer);
useEffect(() => {
if (timerStart === true) {
timer = setInterval(() => {
if (seconds > 0) {
setSeconds(seconds - 1);
}
if (seconds === 0) {
onGameEnd(true);
clearInterval(timer);
}
}, 1000);
}
return () => clearInterval(timer);
});
return (
<p className="time">
Time{" "}
<span className="span1">
<span>{seconds}s</span>
</span>
</p>
);
};
const Timer = React.memo(({ timerStart, timer, onGameEnd, restartTimer }) => (
<Child
timerStart={timerStart}
timer={timer}
onGameEnd={onGameEnd}
restartTimer={restartTimer}
/>
));
export default Timer;
my timer gets re initialized when restartTimer state is set to true.