Create the Match Match/Memory Game in React and Vue.js

Similarities and differences building the same game with two different frameworks

Daan
Daan
Feb 13 · 11 min read

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.

I have experience in both of these frameworks I thought it might be helpful if I documented the process of building the same app in each. The goal is to show you the differences and similarities in code between these two popular JavaScript frameworks. I didn’t want to build a standard app, because I figured that building a game would more fun!


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:

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:

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 public, src, and 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 playfield.

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 .


Passing Props

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 in the Vue example. If you use the colon, the contents of the attribute are evaluated as Javascript; if you don’t, it’s a string. By using the colon 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 useEffect hook.

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 methods object.

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:

A React state variable using the State hook
Vue’s data object

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 data object.

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.

The 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

Since the 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 false.

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 and !loading. In Vue, you can do if-else constructions by using v-if and v-else.

Also, note that looping over an array in React is done by using the map function. Vue has it’s own directive for this: v-for.


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 openCard function.


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:

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!


Optimizations

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 pairVisibleInMilliseconds variable).

Check out the React GitHub repo and the Vue GitHub repo so you don’t have to build from scratch if you don’t want to.


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!

Better Programming

Advice for programmers.

Daan

Written by

Daan

Backend developer from The Netherlands. Crypto enthusiast.

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade