Home > Net >  Setting React array state doesn't update it at all
Setting React array state doesn't update it at all

Time:08-25

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 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.
  • I'm using JSON.stringify() here as a poor man's scoreboard plus debugging tool (for clicked).
  • 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>
  );
}
  • Related