UNPKG

react-card-memory-game

Version:
86 lines (85 loc) 5 kB
import React, { useEffect, useState } from "react"; import ReactFlipCard from "reactjs-flip-card"; import allEqual from "./AllEqual"; import getGeneratedGrid from "./getGeneratedGrid"; import './MemoryGame.css'; import { availableCardStatuses } from "./consts"; function validateGridNumber(gridNumber) { if (!Number.isInteger(gridNumber) || !(gridNumber >= 4 && gridNumber <= 6)) throw new Error("grid number must be an Integer number between 4 and 6"); return null; } export default function MemoryGame({ containerStyle = {}, gridNumber = 4, foundPair = () => undefined, notFoundPair = () => undefined, gameFinished = () => undefined, holeCardsColor = 'orange', foundCardsColor = 'yellow', frontCardsCss = '', backCardsCss = '' }) { useState(validateGridNumber(gridNumber)); const [iconsGrid] = useState(getGeneratedGrid(gridNumber)); // <-- Initialize the grid just one time, only when this component is mounted const [cardStatuses, setCardsStatuses] = useState(iconsGrid.map(_ => availableCardStatuses.hole)); useEffect(() => { const finished = allEqual(cardStatuses, availableCardStatuses.discovered); if (finished) { setTimeout(() => { gameFinished(); }, 200); } // omit gameFinished: we can safely omit function props. // eslint-disable-next-line react-hooks/exhaustive-deps }, [cardStatuses]); // handle user card statuses clicks useEffect(() => { let cardsClickedArray = []; cardStatuses.forEach((status, index) => { const icon = iconsGrid[index]; if (status === availableCardStatuses.clicked) { cardsClickedArray.push({ status, icon }); } }); if (cardsClickedArray.length === 2) { if (cardsClickedArray[0].icon === cardsClickedArray[1].icon) { foundPair(); setTimeout(() => { const newCardStatuses = cardStatuses.map(status => { if (status === availableCardStatuses.clicked) return availableCardStatuses.discovered; return status; }); setCardsStatuses(newCardStatuses); }, 200); } else { notFoundPair(); setTimeout(() => { const newCardStatuses = cardStatuses.map(status => { if (status === availableCardStatuses.clicked) return availableCardStatuses.hole; return status; }); setCardsStatuses(newCardStatuses); }, 700); } } // omit foundPair and notFoundPair: we can safely omit function props. // If not, we have to handle the "onetime" called function with additional flag state // we can safely omit iconsGrid because it's initialized just one time on constructor // eslint-disable-next-line react-hooks/exhaustive-deps }, [cardStatuses]); const getCardStyle = (currentCardStatus) => ({ background: currentCardStatus === availableCardStatuses.discovered ? foundCardsColor : holeCardsColor }); return (React.createElement("div", null, React.createElement("div", { className: `MemoryGame_grid MemoryGame_gridTemplateColumns${gridNumber}`, style: containerStyle }, iconsGrid.map((icon, index) => { const currentCardStatus = cardStatuses[index]; return React.createElement(ReactFlipCard, { key: index, flipCardContainerCss: `${currentCardStatus === availableCardStatuses.discovered ? 'MemoryGame_pairFound' : ''}`, flipByProp: currentCardStatus !== availableCardStatuses.hole, width: "auto", height: "auto", cursor: "pointer", flipTrigger: 'disabled', frontStyle: getCardStyle(currentCardStatus), backStyle: getCardStyle(currentCardStatus), frontCss: `MemoryGame_cardCss ${frontCardsCss}`, backCss: `MemoryGame_cardCss ${backCardsCss}`, onClick: () => { const cardStatusesClone = [...cardStatuses]; const currentCard = cardStatusesClone[index]; const twoCardsClicked = cardStatusesClone.filter(c => c === availableCardStatuses.clicked).length === 2; if (twoCardsClicked || currentCard === availableCardStatuses.clicked || currentCard === availableCardStatuses.discovered) { return; } cardStatusesClone[index] = availableCardStatuses.clicked; setCardsStatuses(cardStatusesClone); }, frontComponent: React.createElement("div", null), backComponent: React.createElement("div", { className: "MemoryGame_padding20" }, icon) }); })))); }