Skip to content

Commit b11ab99

Browse files
committed
fix: resolve score mutation, Timer clearTimeout, and initial options sort in Guess the Flag
1 parent 24cae80 commit b11ab99

2 files changed

Lines changed: 127 additions & 174 deletions

File tree

src/components/Timer.jsx

Lines changed: 27 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,31 @@
1-
2-
import { useEffect } from 'react';
1+
import { useEffect, useRef } from 'react';
32

43
export const Timer = ({ time, setTime, reducer, className, skip, skipf, loss }) => {
5-
let timeout;
6-
useEffect(() => {
7-
if (!timeout) {
8-
if (!skip) {
9-
timeout = setTimeout(() => {
10-
if (time > 0) {
11-
setTime(value => {
12-
return value - 1;
13-
})
14-
}
15-
else if (time === 0) {
16-
loss();
17-
}
18-
}, reducer * 1000);
19-
}
20-
}
21-
}, [time]);
4+
const timeoutRef = useRef(null);
5+
6+
useEffect(() => {
7+
if (skip) return;
8+
timeoutRef.current = setTimeout(() => {
9+
if (time > 0) {
10+
setTime((v) => v - 1);
11+
} else {
12+
loss();
13+
}
14+
}, reducer * 1000);
15+
16+
return () => clearTimeout(timeoutRef.current);
17+
}, [time, skip]);
2218

19+
useEffect(() => {
20+
if (skip) {
21+
clearTimeout(timeoutRef.current);
22+
skipf(false);
23+
}
24+
}, [skip]);
2325

24-
useEffect(() => {
25-
if (skip) {
26-
clearTimeout(timeout);
27-
skipf(false);
28-
}
29-
}, [skip])
30-
return (
31-
<div className={`${className} font-bold h-8 w-8 text-xl dark:bg-white bg-black text-white dark:text-black flex flex-col justify-center items-center rounded-full`}>
32-
{time}
33-
</div>
34-
);
35-
}
26+
return (
27+
<div className={`${className} font-bold h-8 w-8 text-xl dark:bg-white bg-black text-white dark:text-black flex flex-col justify-center items-center rounded-full`}>
28+
{time}
29+
</div>
30+
);
31+
};

src/games/Game.jsx

Lines changed: 100 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -7,183 +7,140 @@ import { Timer } from "../components/Timer";
77
import { getRandomInt, unMemberFilter } from "../utils";
88
import { useGameDataset } from "../hooks/useGameDataset";
99

10+
const sortByName = (a, b) => a.name.common.localeCompare(b.name.common);
11+
1012
export default function Game() {
1113
const dataset = useGameDataset();
12-
const data = unMemberFilter(useContext(DataContext))
13-
const [score, setScore] = useState([0, 0]);
14-
const [results, setResults] = useState([])
15-
const [time, setTime] = useState(10);
16-
const [randomFlag, setRandomFlag] = useState(
17-
dataset[Math.floor(Math.random() * dataset?.length)]
18-
);
19-
const [countries, setCountries] = useState(dataset);
20-
const getOptions = () => {
21-
let tempOptions = [];
22-
while (tempOptions.length < 4) {
23-
let temp = data[getRandomInt(data.length)];
24-
if (tempOptions.indexOf(temp) === -1) {
25-
tempOptions.push(temp);
26-
}
14+
const data = unMemberFilter(useContext(DataContext));
15+
16+
const generateOptions = (correct, pool) => {
17+
const others = pool.filter((c) => c.ccn3 !== correct.ccn3);
18+
const picked = [];
19+
while (picked.length < 4) {
20+
const rand = others[getRandomInt(others.length)];
21+
if (!picked.includes(rand)) picked.push(rand);
2722
}
28-
return tempOptions;
29-
}
30-
const [options, setOptions] = useState([]);
31-
32-
33-
23+
return [...picked, correct].sort(sortByName);
24+
};
3425

26+
const [countries, setCountries] = useState(dataset);
27+
const [randomFlag, setRandomFlag] = useState(
28+
dataset[getRandomInt(dataset.length)]
29+
);
30+
const [options, setOptions] = useState(() =>
31+
generateOptions(randomFlag, data)
32+
);
33+
const [score, setScore] = useState([0, 0]);
34+
const [results, setResults] = useState([]);
35+
const [time, setTime] = useState(10);
3536
const [rmClick, setRmClick] = useState(false);
36-
const handleClick = (event) => {
37-
setRmClick(true)
37+
const [skipFlag, setSkipFlag] = useState(false);
3838

39+
useEffect(() => {
40+
document.title = "Where in the world? - Guess the flag";
41+
}, []);
3942

40-
let target = event.target.innerHTML;
41-
42-
let selected = data.find(value => value.name.common === target);
43-
let tempResult = { options, selected: selected, correct: randomFlag };
44-
let temps = [...results, tempResult];
45-
46-
setResults(temps)
47-
43+
const handleClick = (event) => {
44+
setRmClick(true);
45+
const target = event.target.innerHTML;
46+
const selected = data.find((v) => v.name.common === target);
4847

48+
setResults((prev) => [...prev, { options, selected, correct: randomFlag }]);
4949

5050
if (target === randomFlag.name.common) {
51-
win(true);
51+
win();
5252
} else {
53-
loss(true);
53+
loss();
5454
}
5555

56-
setTimeout(() => {
57-
setRmClick(false);
58-
}, 120);
56+
setTimeout(() => setRmClick(false), 120);
5957
};
6058

61-
const win = (flag) => {
62-
let scoreTemp = score;
63-
scoreTemp[0] = score[0] + 1;
64-
setScore(scoreTemp);
65-
setSkipFlag(flag);
66-
reset();
67-
// setTime(false);
68-
}
69-
70-
const loss = (flag) => {
71-
let scoreTemp = score;
72-
scoreTemp[1] = score[1] + 1;
73-
setScore(scoreTemp);
74-
setSkipFlag(flag);
75-
reset();
76-
}
59+
const win = () => {
60+
setScore((prev) => [prev[0] + 1, prev[1]]);
61+
setSkipFlag(true);
62+
advance();
63+
};
7764

65+
const loss = () => {
66+
setScore((prev) => [prev[0], prev[1] + 1]);
67+
setSkipFlag(true);
68+
advance();
69+
};
7870

79-
const [skipFlag, setSkipFlag] = useState();
80-
81-
const reset = () => {
82-
let countriesTemp = countries.filter((element) => {
83-
return element.ccn3 !== randomFlag.ccn3;
84-
});
85-
setCountries(countriesTemp);
86-
let random = countriesTemp[Math.floor(Math.random() * countriesTemp.length)];
87-
setRandomFlag(random);
88-
let temOptions = data.filter((element) => {
89-
return element.ccn3 !== random.ccn3;
90-
});
91-
let optionsTemp = [];
92-
93-
while (optionsTemp.length <= 3) {
94-
let temp = temOptions[getRandomInt(temOptions.length)];
95-
if (optionsTemp.indexOf(temp) === -1) {
96-
optionsTemp.push(temp);
97-
}
98-
}
99-
optionsTemp.push(random);
100-
101-
optionsTemp.sort((a, b) => {
102-
let x = a.name.common.toLowerCase();
103-
let y = b.name.common.toLowerCase();
104-
if (x < y) {
105-
return -1;
106-
}
107-
if (x > y) {
108-
return 1;
109-
}
110-
return 0;
111-
});
112-
113-
114-
setOptions(optionsTemp);
71+
const advance = () => {
72+
const remaining = countries.filter((c) => c.ccn3 !== randomFlag.ccn3);
73+
setCountries(remaining);
74+
if (remaining.length === 0) return;
75+
const next = remaining[getRandomInt(remaining.length)];
76+
setRandomFlag(next);
77+
setOptions(generateOptions(next, data));
11578
setTime(10);
11679
};
80+
11781
const startAgain = () => {
118-
setRandomFlag(dataset[getRandomInt(dataset.length)])
82+
const first = dataset[getRandomInt(dataset.length)];
11983
setCountries(dataset);
84+
setRandomFlag(first);
85+
setOptions(generateOptions(first, data));
12086
setTime(10);
12187
setRmClick(false);
122-
setScore([0, 0])
123-
setResults([])
124-
setSkipFlag(false)
125-
}
126-
127-
useEffect(() => {
128-
document.title = `Where in the world? - Guess the flag`;
129-
setOptions([...getOptions(), randomFlag])
130-
options.sort((a, b) => {
131-
let x = a.name.common.toLowerCase();
132-
let y = b.name.common.toLowerCase();
133-
if (x < y) {
134-
return -1;
135-
}
136-
if (x > y) {
137-
return 1;
138-
}
139-
return 0;
140-
});
141-
}, []);
88+
setScore([0, 0]);
89+
setResults([]);
90+
setSkipFlag(false);
91+
};
14292

14393
if (dataset.length <= 0) {
144-
return <Navigate replace to={'/games'} />
94+
return <Navigate replace to="/games" />;
14595
}
14696

147-
if (countries.length !== 0) {
148-
// if (false) {
149-
return (
150-
<div className="dark:text-white flex flex-col h-full w-11/12 justify-center items-center mx-auto relative ">
151-
<div className="flex flex-col bg-white/10 backdrop-blur-sm p-10 rounded gap-5 px-5 w-full sm:w-[500px]">
152-
{countries.length >= dataset.length && <h1 className="font-semibold text-2xl">
153-
Choose the name of the country based on the flag
154-
</h1>}
155-
<div className="w-full h-[250px] flex-auto">
156-
<FlagTransition flag={randomFlag} />
157-
</div>
158-
<div className={`flex flex-col gap-x-2 justify-center flex-wrap w-full gap-2 ${rmClick ? 'pointer-events-none' : ''}`}>
159-
{options.map((element, index) => {
160-
return (
161-
<button
162-
key={index}
163-
onClick={handleClick}
164-
className="shadow disabled:bg-invalid rounded p-2 bg-white dark:bg-dark-fe border hover:bg-dark-mode-ligth/10 transition-colors dark:hover:bg-dark-mode-ligth/10 hover:cursor-pointer select-none"
165-
>
166-
{element.name.common}
167-
</button>
168-
);
169-
})}
170-
</div>
171-
<div className="flex flex-row justify-between items-center">
172-
<div className="font-bold">{countries.length}</div>
173-
<Timer reducer={1} time={time} skip={skipFlag} skipf={setSkipFlag} loss={loss} setTime={setTime} className={"self-end"} />
174-
</div>
175-
</div>
176-
</div>
177-
);
178-
} else {
97+
if (countries.length === 0) {
17998
return (
18099
<Modal
181100
title="You finished the game 😁👍🏻"
182-
desc="Congrats! see you score, also you can play again or go back to play more puzzles"
101+
desc="Congrats! See your score, play again or go back to play more puzzles"
183102
again={startAgain}
184103
score={score}
185104
results={results}
186105
/>
187106
);
188107
}
108+
109+
return (
110+
<div className="dark:text-white flex flex-col h-full w-11/12 justify-center items-center mx-auto relative">
111+
<div className="flex flex-col bg-white/10 backdrop-blur-sm p-10 rounded gap-5 px-5 w-full sm:w-[500px]">
112+
{countries.length >= dataset.length && (
113+
<h1 className="font-semibold text-2xl">
114+
Choose the name of the country based on the flag
115+
</h1>
116+
)}
117+
<div className="w-full h-[250px] flex-auto">
118+
<FlagTransition flag={randomFlag} />
119+
</div>
120+
<div className={`flex flex-col justify-center w-full gap-2 ${rmClick ? "pointer-events-none" : ""}`}>
121+
{options.map((element, index) => (
122+
<button
123+
key={index}
124+
onClick={handleClick}
125+
className="shadow rounded p-2 bg-white dark:bg-dark-fe border hover:bg-dark-mode-ligth/10 transition-colors dark:hover:bg-dark-mode-ligth/10 hover:cursor-pointer select-none"
126+
>
127+
{element.name.common}
128+
</button>
129+
))}
130+
</div>
131+
<div className="flex flex-row justify-between items-center">
132+
<div className="font-bold">{countries.length}</div>
133+
<Timer
134+
reducer={1}
135+
time={time}
136+
skip={skipFlag}
137+
skipf={setSkipFlag}
138+
loss={loss}
139+
setTime={setTime}
140+
className="self-end"
141+
/>
142+
</div>
143+
</div>
144+
</div>
145+
);
189146
}

0 commit comments

Comments
 (0)