Create the Match Match/Memory Game in React and Vue.js
Similarities and differences building the same game with two different frameworks
As a child, I loved the game of Match Match, aka Concentration. It’s a pretty simple card game to understand, but once you start playing you quickly realize it can be difficult to remember every card you’ve come across.
All you need for the game is a pack of cards. The rules are simple. All of the cards are laid face down on a surface and two cards are flipped face up over each turn. The object of the game is to turn over pairs of matching cards.
In this piece, we’ll be building the game of Match Match both in React and Vue.
What Should it Look Like?
The image above is the result of this project should be. We won’t focus too much on making it look pretty. You can add additional CSS if you wish. The main goal of this article is to give you some insight into the differences and similarities in code between React and Vue. Feel free to add improvements or new features.
Time to Start!
We’re building our Match Match project from scratch, so we need to create an app first. In React this can be done with the following command:
npx create-react-app match-match
Note that we used the Create React App project for this.
We use the same approach for Vue. We create our project via the Vue CLI:
vue create match-match
Once you get your projects up and running you should see something like this:
That’s pretty easy, isn’t it? That’s all it takes to get started!
Project File Structure
Now that we’ve created both the React and Vue projects, we can take a look at the file structure. Note that this is the file structure of the finished project. However, the structure of the folders is untouched.
Both projects have a
node_modules folder. What’s different is that the Vue project has subfolders in the
src folder. Components are being stored in the
components folder and assets like images are stored in the
assets folder. By default, the React project doesn’t have subfolders but you can make them if you want.
The Vue project has a better structure by default, with fewer files than the React project. Part of this has to do with the fact that no separate stylesheets are needed in the Vue project.
What Do We Need?
First of all, we need a playfield. Then, we need a deck of pairing cards.
Let's start with the first component we’re going to make — the
This is what a basic component looks like in React and Vue, respectively. We can already spot some differences in this example. In our React component, we include a stylesheet by using
import. Our Vue component has its style included with the rest of the logic for the component. There’s no separate stylesheet needed in Vue.
Another subtle difference is that adding a class to an element in React is done by the
className attribute instead of
class, since class is a reserved keyword.
Note that you have the ability to use SCSS instead of CSS. For the sake of keeping things simple, I chose to use CSS in this project .
You’ve probably noticed that the
Playfield component has a prop called
pairs. It’s unused for now, but this prop will tell the
Playfield component how many pairs of cards it should put on the board.
Both projects have an
App component, which is the component at the very top. This renders the
Playfield component and passes the number of pairs via the props.
The way the
Playfield component gets rendered in the
App component is almost identical.
Note the colon that’s used before the
pairs prop gets evaluated as a number.
Fetching Data For the Cards
All cards need an image. And since the number of cards is variable (we added the
pairs prop to the Playfield component) we don’t know how many images we need. That’s why we’re going to use an API to get some images.
We’re going to make use of Axios, which you can install with
npm install axios.
We only want to fetch images from the API once the component is mounted. Vue has a
mounted hook for this, where we call the
getImages function from. In React you can use the
By using this hook, you tell React that your component needs to do something after render. If you don’t specify a second argument the
useEffect hook will run after every render.
If you only want to run the function given to
useEffect after the initial render, you can give it an empty array as the second argument.
The functions in a React component are stored as
const variables. In Vue, on the other hand, they’re defined in the
Once we’ve fetched our images from the API, we want to change the data that we had previously stored. By default, the
images variable is empty — we want to change that now that we’ve fetched the images.
This is a key difference between React and Vue. Vue works pretty straightforwardly. Essentially, it creates a
data object, where data can freely be updated. React, on the other hand, creates a state object where a little more effort is required to carry out updates.
React requires you to make this extra effort for good reason — we’ll dive into that soon. But first, let’s take a look at the following snippets:
You can see that we’ve passed the same
images array into both. In React, the default value of a state variable is passed as the first argument to the
useState function. In Vue, this is done by simply assigning it a value in the
As we’ve mentioned before, how we go about changing this data differs between frameworks. You may have noticed that we can update the
data object directly in Vue. In the
getImages function we directly push an image to the array.
React needs a little more effort. If we want to update the
images state variable, we have to use the
setImages function. So we can’t simply call
images.push(..) and expect the state to update, in contrast to Vue.
What’s the way to go?
Let's take a look at the
getImages function again. We store all the images in a temporary array, called
fetchedImages. Once we have fetched all images, we call the
setImages function with the
fetchedImages as its parameter.
Hooks can be hard to grasp at first — if you want to learn more about them I highly recommend you read the docs.
Generating the Card Deck
Before we can start generating the deck, we need to wait until all images are fetched. In React we can do this by using the
useEffect hook, just as we did to start fetching the images. What’s different here is that we provide the
images variable, instead of an empty array. This means that this hook only gets called when the
images variable is updated.
In Vue, there’s a different way to react to data changes: watch properties. Whenever the
images variable changes the images watch function gets called.
generateCards function is not that exciting in itself. We give each card a unique number, a pair number, an image, and two booleans: open and matched.
The only thing that differs here is the way the state gets updated.
We’ve also added a
shuffleDeck function that randomizes the order of the cards in the deck. This function looks the same for both projects.
Adding a Spinner
getImages function is asynchronous, fetching the data might take a few seconds — especially when you have a lot of pairs because a request is sent for every pair. To make things a little more user-friendly we can add a spinner to show the user that our game is loading.
In the previous example you might have noticed that we sneaked in a
loading variable, which is
true by default. Once the cards are generated this variable is set to
In React there is no way to create an if-else construction in the
return of a component. This leads into two separate checks:
!loading. In Vue, you can do if-else constructions by using
Also, note that looping over an array in React is done by using the
map function. Vue has it’s own directive for this:
The Card Component
As we said before, we also need a deck of cards. But we don’t have a
Card component, yet. This is the last component that we need for this project.
Every card can have one of three different states: open, closed or matched. If it’s matched, we can hide the card. When the card is closed, we show a question mark, if it’s opened we show the image.
Since we can’t do proper logic in the
return of a React component, I’ve chosen to create an extra function,
getCard, that does this job.
The React component has one additional prop,
onCardOpen. This is the callback function that gets passed from the
Playfield component. This is React’s way of calling a parent’s function from a child.
This is done a little differently in Vue. This can be done by emitting a custom event to the parent. In our example, we called the event
onCardOpen and we passed
this.card as an argument.
We can listen for this event in the parent, which is the
Playfield component, and act accordingly. In the mounted hook we listen for the
onCardOpen event. Once this event gets fired the callback will execute, which will call the
Checking for a Match
Once we’ve opened two cards, we want to check if they match. There’s only one new concept in the examples below — can you spot it?
The only thing we haven’t touched on yet is the following line:
let newDeck = [...deck]
To detect a change in this array state variable you can’t simply do the following:
let newDeck = deck. Since the updated deck looks a lot like the old deck, we need to assign it using the spread operator, so that a change in the array gets detected by React.
Handling a Possible Match
I wanted to add a competitive element to the game, so I’ve introduced a counter for the number of turns. To give the user some more information we’ll also provide the number of pairs that are matched. This piece of code is nearly identical.
Once the user has matched all pairs we want the user to be able to start a new game, so we also added this functionality. Seeing this code shouldn’t shock you anymore!
It takes a few seconds before the game starts. This is caused by the way that images are being fetched. I’ve used the Lorem Picsum API, which does not support rendering small images in bulk. There’s an option that lets you download a list of images, but it doesn’t allow you to control the size of the images.
I’ve tried fetching images in bulk, thus sending one request, instead of one request for every pair. This resulted in the game loading very quickly. However, the game became almost unplayable, because some images were over 5MB. For this example, I sacrificed some initial loading speed to improve the gameplay.
This problem can be solved in multiple ways. The first is to add an image set to your project so that you don’t have to fetch it every time you want to start a new game. That’s not quite as fun — the current solution provides you with random images every game. The second way to fix this is by finding an API that lets you get small images in bulk.
Going the Extra Mile
If you really want to go the extra mile, you could build an interface that lets the user set some options for the game, such as the number of pairs and the level of difficulty. You could tweak the level of difficulty by adjusting the time how long a pair is visible (the
Part of a Series
I’m thinking about making a series of pieces that contain content just like this article. In each piece in this series, I’ll be doing a small coding project that dives into the differences between certain programming languages, frameworks, or techniques.
If you have any suggestions for the next part of this series, please let me know.
Your feedback is much appreciated!