在React中不能切换所有元素

所以在点击时,它只能将一张卡翻转回来。所以在我的函数中正确地读取了toggle,但我不明白为什么它没有把两张牌都翻过来,它只翻转了我的数组中位置[1]的牌,而位置[0]是不受影响的,当只是翻转牌时,在翻转函数中,它把两张牌都翻转了,但似乎当我设置数组时,我漏掉了什么。此外,如果我试图做一些像cardChoiceId[0].setToggle(false)这样的事情,我得到一个类型错误,说不是一个函数,所以这不是解决方案,下面是代码

import bg from '../images/card-bg.png'

let cardChoiceId = []
let cardsWon = []

const GameCard = ({ img, name, num, setScore, setResult, cardArray }) => {
    const [toggle, setToggle] = useState(false)




    const checkForMatch = () => {
        if (cardChoiceId[0] === cardChoiceId[1]) {
            alert(`🌱That's a Match🌱`)
            setToggle(true)
            cardsWon.push(cardChoiceId)
            cardChoiceId = []
            setScore(+1)
            if (cardsWon.length === cardArray.length) {
                setResult(`🎆 Congratz!!! You Win 🎆`)
            }   
        }
        else {
            setToggle(false)
            cardChoiceId = []
        }   
    }

    const flip = () => {
        setToggle(!false)
        cardChoiceId.push(name)
        console.log(cardChoiceId)
        if (cardChoiceId.length === 2) {
            return setTimeout(checkForMatch, 500)
        }

    }



    return toggle === false ? (
            <div onClick={flip}>
                <div className='game-card-bg' >
                    <img src={bg} alt='card' style={{ width: '100%' }} />
                </div>
             </div>
            ):(
            <div>
        <div className='game-card' >
            <img src={img} alt='card' />
            <h4 className='card-name'>{name}</h4>
        </div>  
    </div>
    )

}

export default GameCard

解决方案:

问题在于,你试图在本地组件状态下管理游戏状态,并将组件状态模式与全局状态(“cardChoiceId`等)混合。让我把话说得更明白些,你应该遵循的是 分清主次 原则。

你有 游戏 国家应该跟踪有多少张牌是toggeledflipped并检查它们是否匹配。

你有 本地 国情 GameCard 组件,负责渲染游戏卡的任一状态(切换或不切换)。首先,看起来你的本地状态应该管理切换状态,但你的全局状态取决于切换状态,所以它必须被 “拉升”。因此,在 GameCard.

我更建议外部组件应该告诉它是否被翻转,同时外部组件也应该提供一个回调,组件可以用它来通知外部组件一个点击事件。请注意,如何推理每个组件发生的事情变得更加容易。

首先,想一想一张卡可以处于的状态,对我来说,这将是:

const CardState = {
  HIDDEN: 0,
  FLIPPED: 1,
  WON: 2,
};

让我们假设一个… cardArray 洗牌后的牌阵,每个名字都出现两次

[
  {
    id: 1,
    name: 'Tiger',
    img: '...',
  },
  {
    id: 2,
    name: 'Lion',
    img: '...',
  },
  {
    id: 3,
    name: 'Tiger',
    img: '...',
  },
  {
    id: 4,
    name: 'Lion',
    img: '...',
  },
  ...
]

现在,我们可以实现我们的 Game 逻辑,但我们并不关心如何显示卡片本身。我们只关心这里的逻辑。

const Game = ({ cardArray }) => {
  // keep track of every cards state
  const [cardState, setCardState] = useState(
    // initialize every card as HIDDEN
    cardArray.reduce((a, c) => ({
      ...a,
      [c.id]: {
        ...c, // all card info
        state: CardState.HIDDEN, // add state
      },
    }, {})
  );

  // set the new state of a specific card
  const updateState = (id, newState) => setCardState(prev => {
    ...prev, // keep state of all cards
    [id]: {
      ...prev[id],
      state: newState, // update state of id
    },
  });

  // check if all cards are WON
  const checkGameOver = () => {
    const notWonCard = Object.values(cardState)
      .find(card => card.state !== CardState.WON);
    if (!notWonCard /* ===  all won*/) {
      alert(`🎆 Congratz!!! You Win 🎆`);
    }
  };

  // checks if two FLIPPED cards match and if so, set them both WON, in this case also trigger a checkGameOver
  const checkForMatch = () => {
    const flippedCards = Object.values(cardState)
      .filter(card => card.state === CardState.FLIPPED);
    if (flippedCards.length === 2) {
      if (flippedCards[0].name === flippedCards[1].name) {
        alert(`🌱That's a Match🌱`);
        updateState(flippedCards[0].id, CardState.WON);
        updateState(flippedCards[1].id, CardState.WON);
        setTimeout(checkGameOver, 500);
      }
    }
  };

  // update the state of a card depending on its current state and trigger checkForMatch
  const onCardClick = id => {
    switch (cardState[id].state) {
      case CardState.FLIPPED: {
        updateState(id, CardState.HIDDEN);
        break;
      }
      case CardState.HIDDEN: {
        updateState(id, CardState.FLIPPED);
        setTimeout(checkForMatch, 500);
        break;
      }
      case CardState.WON: 
        // fallthrough
      default: {
        // noop
      }
    }
  };

  return (
    <div>
      {cardArray.map(card => (
        <GameCard
          key={card.id}
          name={card.name}
          img={card.img}
          isFlipped={
            cardState[card.id] === CardState.FLIPPED
              || cardState[card.id] === CardState.WON
          }
          onClick={() => onCardClick(card.id)}
        />
      ))}
    </div>
  );
}

现在逻辑已经完成了,我们可以不用管它,去想实际的卡片应该如何渲染。

import bg from '...';
const GameCard = ({ name, img, isFlipped, onClick }) => (
  isFlipped
  ? (
    <div>
      <div className='game-card' >
        <img src={img} alt='card' />
        <h4 className='card-name'>{name}</h4>
      </div>  
    </div>
  ) : (
    <div onClick={onClick}>
      <div className='game-card-bg' >
        <img src={bg} alt='card' style={{ width: '100%' }} />
      </div>
    </div>
  )
);

这不是经过测试的代码,而是给你一个分离关注点的概念。

给TA打赏
共{{data.count}}人
人已打赏
未分类

当使用模块时,使VSCode在工作空间中安装包。

2022-9-21 0:33:37

未分类

如何使用DataGrip SQL formatter和UNLOAD语句

2022-9-21 0:33:39

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索