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>
);
}
CodePudding user response:
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 callshandleCardClick
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 thatuseState
allows a function initializer; i.e.initializeCards
will be called once by React when Playboard mounts.useEffect
is used to (possibly) updatebestScore
whenscore
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.- I'm using
JSON.stringify()
here as a poor man's scoreboard plus debugging tool (forclicked
). - I'm making liberal use of the functional update form of
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>
);
}