Reputation: 63
I've been stuck with this idea of setting the array to an empty one after the function fires, but no matter what I did the results were the same. I want to empty an array if it contains the element that was pressed, but It keeps logging the old array, making it impossible to check if array already contains it in the first place. I'm new to React and I've already tried using useEffect() but I don't understand it that much even after studying and trying to use it several times.
import React, { useState, useEffect } from "react";
import { Card } from "./Card";
import "../styles/Playboard.css";
import { Scoreboard } from "./Scoreboard";
export function Playboard(props) {
const [score, setScore] = useState(0);
const [bestScore, setBestScore] = useState(0);
const [clicked, setClicked] = useState([]);
const [cards, setCards] = useState([
<Card name="A" key={1} handleCardClick={handleCardClick}></Card>,
<Card name="B" key={2} handleCardClick={handleCardClick}></Card>,
<Card name="C" key={3} handleCardClick={handleCardClick}></Card>,
<Card name="D" key={4} handleCardClick={handleCardClick}></Card>,
<Card name="E" key={5} handleCardClick={handleCardClick}></Card>,
<Card name="F" key={6} handleCardClick={handleCardClick}></Card>,
]);
const increment = () => {
setScore((c) => c + 1);
};
function checkScore() {
if (score > bestScore) {
setBestScore(score);
}
}
function handleStateChange(state) {
setClicked(state);
}
useEffect(() => {
setCards(shuffleArray(cards));
handleStateChange();
}, []);
useEffect(() => {
setCards(shuffleArray(cards));
checkScore();
}, [score]);
function handleCardClick(e) {
console.log(clicked);
if (clicked.includes(e.target.querySelector("h1").textContent)) {
console.log(clicked);
resetGame();
} else {
increment();
clicked.push(e.target.querySelector("h1").textContent);
console.log(clicked);
}
}
function resetGame() {
setScore(0);
setClicked([]);
console.log(clicked);
}
const shuffleArray = (array) => {
return [...array].sort(() => Math.random() - 0.5);
};
return (
<div>
<div className="container">{cards}</div>
<Scoreboard score={score} bestScore={bestScore}></Scoreboard>
</div>
);
}
Upvotes: 0
Views: 901
Reputation: 169407
Looks like this is a memory game of sorts, where you have to remember which cards you've clicked and they shuffle after every click? Okay!
As my comment says, you don't want to put JSX elements in state; state should be just data, and you render your UI based on that.
Here's a cleaned-up version of your game (here on CodeSandbox) –
Card
is a simple component that renders the name of the card, and makes sure clicking the button calls handleCardClick
with the name, so you need not read it from the DOM.shuffleArray
doesn't need to be in the component, so it's... not.initializeCards
returns a shuffled copy of the A-F array.useState(initializeCards)
takes advantage of the fact that useState
allows a function initializer; i.e. initializeCards
will be called once by React when Playboard mounts.useEffect
is used to (possibly) update bestScore
when score
updates. You don't really need it for anything else in this.cards.map(... => <Card...>)
is the usual React idiom to take data and turn it into elements.JSON.stringify()
here as a poor man's scoreboard plus debugging tool (for clicked
).setState
for efficiency (not that it matters a lot in this).import React, { useState, useEffect } from "react";
function Card({ name, handleCardClick }) {
return <button onClick={() => handleCardClick(name)}>{name}</button>;
}
function shuffleArray(array) {
return [...array].sort(() => Math.random() - 0.5);
}
function initializeCards() {
return shuffleArray(["A", "B", "C", "D", "E", "F"]);
}
function Playboard() {
const [score, setScore] = useState(0);
const [bestScore, setBestScore] = useState(0);
const [clicked, setClicked] = useState([]);
const [cards, setCards] = useState(initializeCards);
useEffect(() => {
setBestScore((bestScore) => Math.max(bestScore, score));
}, [score]);
function handleCardClick(name) {
if (clicked.includes(name)) {
resetGame();
} else {
setScore((c) => c + 1);
setClicked((c) => [...c, name]);
setCards((cards) => shuffleArray(cards));
}
}
function resetGame() {
setScore(0);
setClicked([]);
setCards(initializeCards());
}
return (
<div>
<div className="container">
{cards.map((name) => (
<Card name={name} key={name} handleCardClick={handleCardClick} />
))}
</div>
{JSON.stringify({ score, bestScore, clicked })}
</div>
);
}
Upvotes: 1