I have a small memory game that I am building to learn React. The idea is that you click a tile, it is shuffled, and you try to pick one you haven't already selected. The problem is I can't seem to get my internal state for which tiles have previously been seen to update.
Here is my component:
const Gameboard = (props) => {
const [tiles, setTiles] = useState([]);
const [currentScore, setCurrentScore] = useState(0);
const [highScore, setHighScore] = useState(0);
const [previouslySeen, setPreviouslySeen] = useState([]);
const handleClick = (evt) => {
const tile = evt.target;
if (!previouslySeen.includes(tile)) {
setCurrentScore(current => current + 1);
props.setCurrentScore(current => current + 1);
setPreviouslySeen([...previouslySeen, tile]);
} else {
alert("Game over, thanks for playing!");
if (currentScore > highScore) {
setHighScore(currentScore);
props.setHighScore(currentScore);
}
setCurrentScore(0);
props.setCurrentScore(0);
setPreviouslySeen([]);
}
};
const generateTiles = (tileCount) => {
return [...Array(tileCount).keys()].map((n) => {
return <Tile key={n} number={n} handleClick={handleClick} />;
});
};
const shuffleTiles = (tiles) => {
let tilesCopy = [...tiles];
for (let i = tilesCopy.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * i);
const temp = tilesCopy[i];
tilesCopy[i] = tilesCopy[j];
tilesCopy[j] = temp;
}
return tilesCopy;
};
useEffect(() => {
const initialTiles = generateTiles(props.tileCount);
setTiles(shuffleTiles(initialTiles));
}, []);
useEffect(() => {
setTiles((oldTiles) => {
const newTiles = shuffleTiles(oldTiles);
return newTiles;
});
}, [currentScore, highScore]);
return <div className="Gameboard">{tiles}</div>;
};
The handleClick
method gets passed into the individual Tile
components and is just called with onClick
. The handleClick method is what is giving me trouble, it seems as if it doesn't ever update
previouslySeen`, and I have similar problems with setting the high score in the parent component so that it is reflected in the UI. How are you supposed to handle updates like this in React?
Try this:
const handleClick = (evt) => {
const tile = evt.target;
if (!previouslySeen.includes(tile)) {
setCurrentScore(current => current + 1);
props.setCurrentScore(current => current + 1);
// Change the following line:
setPreviouslySeen(previousState => [...previousState, tile]);
} else {
Also, you're getting setCurrentScore, setHighScore
from parent props and from declaring states:
const [currentScore, setCurrentScore] = useState(0);
const [highScore, setHighScore] = useState(0);
So essentially you're keeping two separate currentScore
and highScore
states - one in the Gameboard
component, and one in its parent. It's best to delete these state variables and setters from Gameboard
and get them directly from parent props.
It turned out that it was because I was storing JSX elements directly in state. Removing that and instead just storing the tile value (plain JS strings) fixed the issue where state wasn't being correctly updated. Quite the gotcha!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.