Переключить в React, а не Переключить все элементы - PullRequest
0 голосов
/ 28 апреля 2020

Таким образом, при нажатии только одна карта переключается обратно. Таким образом, в моих функциях правильно читается тумблер, но я не понимаю, почему он не переворачивает обе карты поверх него, а только переворачивает карту в позицию [1] в моем массиве, а позиция [0] не затрагивается, если просто перевернуть карты в ложь функция flip переворачивает обе карты, но когда я устанавливаю массив, мне кажется, что я что-то упустил. также, если я пытаюсь сделать что-то вроде 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

1 Ответ

0 голосов
/ 28 апреля 2020

Проблема в том, что вы пытаетесь управлять состоянием игры параллельно с локальным состоянием компонента и смешивать шаблоны состояний компонента с глобальным состоянием (`` cardChoiceId` et c). Позвольте мне прояснить это, вы должны следовать принципу разделения проблем .

Вы получили игру состояние, которое должно отслеживать, сколько карт было переключено / перевернуто и проверьте, совпадают ли они.

У вас есть локальное состояние компонента 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 logi c, но нам все равно, как отображать карту сам. Мы заботимся только о логике c здесь.

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>
  );
}

Теперь, когда логи c сделаны, мы можем не думать о том, как должна отображаться действительная карта:

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>
  )
);

Это не проверенный код, скорее, он должен дать вам представление о разделении проблем.

...