Проблема в том, что вы пытаетесь управлять состоянием игры параллельно с локальным состоянием компонента и смешивать шаблоны состояний компонента с глобальным состоянием (`` 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>
)
);
Это не проверенный код, скорее, он должен дать вам представление о разделении проблем.