Creating A Memory Game With React

Shuvo - Nov 29 '21 - - Dev Community

In this article I will explain how I created this Memory Game with React JS
React.JS card game
Basically what happens in this game is You will have some cards and when you click on them the card flips and reveals the image that it has. We have to try to find cards in pair(cards with same image).

I have a step by step tutorial for this project on my YouTube channel you can check it out.

But if you just want a overview stick around.

Okay so first I created a Cards.js components. That will render all the cards.

// components/Cards.js
import Card from './Card'

function Cards(){
    // Items is a array of objects
    // Each object has id, two objects have same id. We will use them to check if the user have found a pair
    // They also have a img property. I have put all the images in public/img folder
    // Thier stat proprty indicates in what status/state the card is currently on
    // stat == "" means the card is not flipped so the image is hidden
    // stat == "active" means the card is flipped so the image is visible
    // stat == "correct" means we found a pair so the card will be flipped and highlighted in green color
    // stat == "wrong" means we choose 2 different type of cards so the card will be flipped and highlighted in red color
    const [items, setItems] = useState([
        { id: 1, img: '/img/html.png', stat: "" },
        { id: 1, img: '/img/html.png', stat: "" },
        { id: 2, img: '/img/css.png', stat: "" },
        { id: 2, img: '/img/css.png', stat: "" },
        { id: 3, img: '/img/js.png', stat: "" },
        { id: 3, img: '/img/js.png', stat: "" },
        { id: 4, img: '/img/scss.png', stat: "" },
        { id: 4, img: '/img/scss.png', stat: "" },
        { id: 5, img: '/img/react.png', stat: "" },
        { id: 5, img: '/img/react.png', stat: "" },
        { id: 6, img: '/img/vue.png', stat: "" },
        { id: 6, img: '/img/vue.png', stat: "" },
        { id: 7, img: '/img/angular.png', stat: "" },
        { id: 7, img: '/img/angular.png', stat: "" },
        { id: 8, img: '/img/nodejs.png', stat: "" },
        { id: 8, img: '/img/nodejs.png', stat: "" }
    ].sort(() => Math.random() - 0.5)) // shuffling the array using the sort method

    // This function will be called when the user click on a card
    function handleClick(id){
        // just for testing
        alert(id)
    }

    return (
        <div className="container">
            { items.map((item, index) => (
                <Card key={index} item={item} id={index} handleClick={handleClick} />
            )) }
        </div>
    )
}

export default Cards
Enter fullscreen mode Exit fullscreen mode

Then I created the Card.js component.

// components/Card.js
function Card({item, id, handleClick}){
    // geting classes based on status of the card
    const itemClass = item.stat ? " active " + item.stat : ""

    return (
        <div className={"card" + itemClass} onClick={() => handleClick(id)}>
            <img src={item.img} alt="" />
        </div>
    )
}

export default Card
Enter fullscreen mode Exit fullscreen mode

And finally I added the JavaScript game logic in Cards.js component

// components/Cards.js
import { useState } from 'react'
import Card from './Card'

function Cards(){
    const [items, setItems] = useState([
        { id: 1, img: '/img/html.png', stat: "" },
        { id: 1, img: '/img/html.png', stat: "" },
        { id: 2, img: '/img/css.png', stat: "" },
        { id: 2, img: '/img/css.png', stat: "" },
        { id: 3, img: '/img/js.png', stat: "" },
        { id: 3, img: '/img/js.png', stat: "" },
        { id: 4, img: '/img/scss.png', stat: "" },
        { id: 4, img: '/img/scss.png', stat: "" },
        { id: 5, img: '/img/react.png', stat: "" },
        { id: 5, img: '/img/react.png', stat: "" },
        { id: 6, img: '/img/vue.png', stat: "" },
        { id: 6, img: '/img/vue.png', stat: "" },
        { id: 7, img: '/img/angular.png', stat: "" },
        { id: 7, img: '/img/angular.png', stat: "" },
        { id: 8, img: '/img/nodejs.png', stat: "" },
        { id: 8, img: '/img/nodejs.png', stat: "" }
    ].sort(() => Math.random() - 0.5))

    // prev is short for previous
    // prev will store the id of previously clicked card
    const [prev, setPrev] = useState(-1)

    function check(current){
        if(items[current].id == items[prev].id){
            // if previously clicked card is same as current card
            // set the stat of both cards to correct
            items[current].stat = "correct"
            items[prev].stat = "correct"
            setItems([...items])
            setPrev(-1)
        }else{
            // if previously clicked card is same not as current card
            // set the stat of both cards to wrong
            items[current].stat = "wrong"
            items[prev].stat = "wrong"
            setItems([...items])

            // After 1 second, set the stat of both cards to ""
            // So they would flip back
            setTimeout(() => {
                items[current].stat = ""
                items[prev].stat = ""
                setItems([...items])
                setPrev(-1)
            }, 1000)
        }
    }

    function handleClick(id){
        if(prev === -1){ // if prev is -1, then it means no card was clicked previously.
            items[id].stat = "active"
            setItems([...items])
            setPrev(id)
        }else{
            check(id)
        }
    }

    return (
        <div className="container">
            { items.map((item, index) => (
                <Card key={index} item={item} id={index} handleClick={handleClick} />
            )) }
        </div>
    )
}

export default Cards
Enter fullscreen mode Exit fullscreen mode

you can find the complete coded here.

Make sure you checkout my other articles and YouTube channel

0shuvo0 image

Was it helpful? Support me on Patreon

Patreon Logo

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .