Home > Mobile >  Problem with setState in my app. Why isClickable property sets back on true on its own?
Problem with setState in my app. Why isClickable property sets back on true on its own?

Time:08-10

I want to make a card memory game. When two cards are flipped isClickable needs to change to false in order for other cards to be unclickable, and that's what happens. But, at the end isClickable is always set to true. I don't know why. Here's my code.

const Card = ({ number, flipped, flipHandler, index }) => {
  return (
    <div
      className={flipped ? "card disabled" : "card"}
      data-index={index}
      onClick={flipHandler}
    >
      {flipped ? number : "FLIP ME"}
    </div>
  );
};

const Game = () => {
  const [cardState, setCardState] = React.useState({
    gameState,
    isClickable: true
  });

  function handleClick(e) {
    let newGameState = cardState.gameState.map((card, index) => {
      if (index ===  e.target.dataset.index) {
        card.isFlipped = !card.isFlipped;
      }

      return card;
    });

    setCardState({ ...cardState, gameState: newGameState });
    if (checkGame(cardState.gameState)) {
      setCardState({ ...cardState, isClickable: false });
      setTimeout(() => {
        let flippedCards = cardState.gameState.filter((card) => card.isFlipped);
        if (flippedCards[0].number === flippedCards[1].number) {
          const newGameState = cardState.gameState.filter(
            (card) => card.number !== flippedCards[0].number
          );
          setCardState({ ...cardState, gameState: newGameState });
        } else {
          const newGameState = cardState.gameState.map((card) => {
            return { ...card, isFlipped: false };
          });
          setCardState({ ...cardState, gameState: newGameState });
        }
      }, 1000);
    }
  }

  let cards = cardState.gameState.map((card, index) => {
    return (
      <Card
        number={card.number}
        flipHandler={
          cardState.isClickable
            ? handleClick
            : function () {
                return false;
              }
        }
        flipped={card.isFlipped}
        index={index}
      />
    );
  });

  return <>{cards}</>;
};

CodePudding user response:

To explain why it doesn't work now.

Simplified example:

// this runs as expected
setCardState({...cardState, isClickable: false});
setTimeout(()=>{
  // now after 1000 ms your anonymous function see the old `cardState`
  // that's because react creates new objects every time a component renders
  setCardState({...cardState, gameState: newGameState});
  // using this instead gives access to the newest state every time
  setCardState(cardState => {...cardState, gameState: newGameState});
}, 1000);
  • Related