0

I have the following component:-

import React, { useState, useEffect } from "react"
import Card from "./Card"

import joker from "../../assets/joker.png"
import goalie from "../../assets/goalie.png"
import defender from "../../assets/defender.png"
import midfielder from "../../assets/midfielder.png"
import attacker from "../../assets/attacker.png"

const CardsBoard = () => {

  const deckLimits = {
    joker: 4, goalkeepers: 12, defenders: 12, midfielders: 12, attackers: 12
  };

  const [ratingObj, setRatingObj] = useState({});
  const [visible1, setVisible1 ] = useState(false);
  const [visible2, setVisible2 ] = useState(false);
  const [deck, setDeck] = useState([]);
  const [deck1, setDeck1] = useState([]);
  const [deck2, setDeck2] = useState([]);
  const [currCardPl1, setCurrCardPl1] = useState({});
  const [currCardPl2, setCurrCardPl2] = useState({});

  useEffect(() => {
    generateDeck();
    distributeCards();
  }, [deck]);


  const startGame = () => {
    //reset decks
    setDeck([]);
    setDeck1([]);x
    setDeck2([]);

    //generate the card’s values
    generateDeck();
    if (deck.length > 0) {
      distributeCards();

      if (currCardPl1 != undefined){
        console.log(currCardPl1);
        //unMask ratings of Player 1
        setVisible1(true);
        setVisible2(false);
      }
    }
  };

  const distributeCards = () => {
      //randomize the deck
      deck.sort(() => Math.random() - 0.5);

      //distribute 26 cards at random when the game start
      const splitDeck1 = deck.slice(0, 26);
      const splitDeck2 = deck.slice(26, 52);
      //add this card to the deck
      splitDeck1.map((card) => {
        setDeck1(deck1 => [...deck1, card]);
      });
      //add this card to the deck
      splitDeck2.map((card) => {
        setDeck2(deck2 => [...deck2, card]);
      });
      
      //queue the first card to Player 1
      setCurrCardPl1(deck1[0]);      
      //queue the first card to Player 2
      setCurrCardPl2(deck2[0]);      
  };

  const generateDeck = () => {
    for (let i = 0; i < deckLimits.joker; i++) {
      generateCard('joker');
    };
    for (let i = 0; i < deckLimits.goalkeepers; i++) {
      generateCard('goalkeeper');
    };
    for (let i = 0; i < deckLimits.defenders; i++) {
      generateCard('defender');
    };
    for (let i = 0; i < deckLimits.midfielders; i++) {
      generateCard('midfielder');
    };
    for (let i = 0; i < deckLimits.attackers; i++) {
      generateCard('attacker');
    };
  }

  const generateCard = item => {
    const card = {
      player: 0,
      image: getImage(item),
      title: item.toUpperCase(),
      ratings: [
        { title: "Handling", rating: "99" },
        { title: "Reflexes", rating: "99" },
        { title: "Defending", rating: "99" },
        { title: "Strength", rating: "99" },
        { title: "Passing", rating: "99" },
        { title: "Flair", rating: "99" },
        { title: "Finishing", rating: "99" },
        { title: "Composure", rating: "99" },
      ],
    }

    //add this card to the deck
    setDeck(deck => [...deck, card]);
  }

  const getImage = item => {
    switch (item) {
      case "joker":
          return joker;
      case "goalkeeper":
          return goalie;
      case "defender":
          return defender;
      case "midfielder":
          return midfielder;
      case "attacker":
          return attacker;
      default:
        break;
    }
  };

  return (
    <div className="container-fluid justify-content-center">
      <div className="row">
        <div className="col-md-6">
          <div>
            <h4>Player 1</h4>
          </div>
          <Card
            cardInfo={currCardPl1}
            showRatings={visible1}
            onClick={ratingObj => setRatingObj(ratingObj)}
          />
        </div>
        <div className="col-md-6">
          <h4>Player 2</h4>
          <Card
            cardInfo={currCardPl2}
            showRatings={visible2}
            onClick={ratingObj => setRatingObj(ratingObj)}
          />
        </div>
      </div>
      <div className="row top-buffer">
        <div className="col-md-12 text-center">
          <button
            id="startGameBtn"
            name="StartGameButton"
            className="btn btn-secondary"
            onClick={() => startGame()}
          >
            START GAME
          </button>
        </div>
      </div>
    </div>
  )
}

export default CardsBoard

And I am getting the error:-

Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

I am suspecting it has to do something with the Submit button (onClick) which is calling the setState multiple times, however I cannot seem to fix the problem.

So I have the following questions :-

  1. How can I fix the onClick()?
  2. I would like to click on the Start Game button, and trigger generateDeck(); and distributeCards(); so that I get the deck, deck1 and deck2 filled up. Is there a way to do it? At the moment I have created a useEffect() which is dependant on the deck being filled up, is that the correct way to doing it with hooks?

Thanks for your help and time!

2
  • Once you call generateDeck it will change deck which will trigger an infinite loop in useEffect. See in generateCard. Commented Sep 13, 2020 at 19:18
  • Is there a reason to add deck to the useEffect dependencies? do you want the deck updated only on page load? if so, maybe that dependency list should be []? Commented Sep 13, 2020 at 19:19

3 Answers 3

3

You have deck as a dependency in your useEffect hook. That means generateDeck will run every time the deck is updated. However, generateDeck calls generateCard, which calls setDeck. That causes deck to change, which means you're stuck in a sort of infinite loop. You should tie generateDeck to the onClick handler or some other event which will not be triggered by deck changing.

Also, be careful with this:

deck.sort(() => Math.random() - 0.5)

Calling sort will mutate your state directly, which is something you should avoid in React. Try something like setDeck(d=>[...d].sort(/* Your sorting function here */)) instead.

Sign up to request clarification or add additional context in comments.

4 Comments

How can I tie the generateDeck() useEffect on the onClick handler?
Also removing the dependency on deck[] in the useEffect() is causing me to click the Start Game button 3 times to get data. Is it possible to be clicked once and load the decks correctly?
If you have the generateDeck function as an onClick handler, you won't need it in useEffect. As for needing to click the button more than once, it might have to do with the fact that generateDeck doesn't distribute the cards unless the deck's length is greater than 0. You might have to rethink the order of those function calls and what triggers them
Ok Danilo I will give you the answer. I will have to rip my code apart, and build it again one step at a time. Thanks for your help
0

You are using setDeck() in distrubuteCards and in your useEffect hook, you call again the distributeCards

  const distributeCards = () => {
  // bla bla
       setDeck(deck => [...deck, card]);
  }   

Which results in calling the hook all the time.

useEffect(() => {
    generateDeck();
    distributeCards();
  }, [deck]);

Comments

0
deck.sort(() => Math.random() - 0.5);

You have a problem here. Save it inside a variable. Otherwise you update the state all the time it's called.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.