Creating Markers on Google Maps with React + Typescript

This is the part two of my Google Maps series. I don’t truly know where we are heading with this app, but I will keep teaching you the secrets of Google Maps programming with React and Typescript. Few days ago, I released the last video. This article serves as a cheatsheet of what I implemented on Youtube.

Creating Markers on Google Maps with React + Typescript (Part 2)

Link to GitHub Respository 

IMarker Interface and marker hook

First, we should create an interface for our marker, right? I want to keep three basic data attributes for a marker. Of course, the label of a marker could be also added here as another string value, but I want to keep things simple. Maybe we do it in the next video.

Based on this interface, I also create the marker hook which will store the content.

const [marker, setMarker] = useState<IMarker>();

Event Listener

While working with Google Maps on React, you should still write some Vanilla JS code as well. I don’t like this, but that’s the way it is. As soon as the map hook is initialised, we add a click listener into this function. This event listener will return us a latLng event object which will be used to get the coordinates from geocoder.

coordinateToAddress

As you notice, I invoke a function called coordinateToAddress() whenever the user clicks somewhere on the map. The click event of google.maps namespace returns us a typical event (e), but this e event has a special attribute called latLng. This is our location value. In this function, I pull three different data types from geocoder. I get the address, latitute and longitude. Just what I need to update with my marker hook.

At this point of the program, our Map component successfully retrieves the coordinates of clicked point and it stores the data in our hook (marker), but we do not illustrate any marker to the user, yet.

addSingleMarker

I created this function just for convention. If we decide to create an array of IMarker objects (which means storing several markers on the map at the same time), we would create another function called addMultipleMarkers. But let’s see what addSingleMarker() does first. It simply follows the changes on marker hook with a useEffect and whenever it is updated we invoke addMarker function.

addMarker

So, here is a much more reusable function. This is actually the place where our Map component will create the marker and it will show it to the user.

google.maps.Marker receives a JSON object where the options are almost limitless. There are so many attributes that you can use to customise your marker, but I only used fundamental parts here (position, map and icon). I also created a dummy function called getIconAttributes() which is only used for the output of icon. You may refer to Google Maps manual for further customisation.

Task

The gif animation below shows how I create markers on the map, but there is a problem. As I click a point on the map, the component successfully creates a marker. However, when I create once more, the previous marker is still there although the marker hook changed its value. So, try to create a function which will remove the previous function. Good luck!

I hope you enjoyed this post. I will keep on writing more about Google Maps. Take care!

Sercan Leylek / OSLO

Setting up Google Maps with React + Typescript

Coding with Google Maps is a fun, mysterious and challenging journey. If you do it with Typescript, both joy and pain get doubled.

When I was coding a Google Maps component at Fremtind fremtind logo, I was starving for a comprehensive tutorial on this topic. Unfortunately, the results were a bit disappointing, but like all developers I found a solution for each problem and the job was done. Then I asked to myself: Why not creating a comprehensive project and delivering all the secrets of my journey in one post?

Meanwhile I was working for my map component, I discovered the answers to my questions via different websites. For example:

  • How can I use typescript types from Google for my map project in React?
  • How should I load my Google Maps Api script into my homepage? What could be the best practice?
  • Where should I use “useRef” to point out my Google Maps DOM element?
  • ….

Some of the answers that I found were outdated, some people were not quite sure of what they were advising, etc… You know the drill.

As a result, I created a sample project at my GitHub repository and decided to sail for a new adventure as well. For the first time, I screencast a coding experience like an experienced youtuber. The duration of the video is 55 minutes and I let the audience view the whole process while I am coding the React project.

Link to GitHub Respository github-169-1174970

CODING NOTES

Setup and Installation

To kick off the project, we need to create a react application with typescript flag. Use the sample command below to start with:

npx create-react-app google-maps --template typescript

After cleaning up default stuff from the project (like deleting logo.svg, App.test.tsx, etc), run the command below to install Google Maps typescript types into your project as a node module element. This will help you access types from google.maps namespace. (Such as google.maps.Map, google.maps.LatLng, google.maps.MapTypeId, …)

npm install --save-dev @types/googlemaps

Bonus Hint: By doing so, you will also obtain google.maps.places.AutocompletePrediction type which can be used to build an address dropdown component.

Now we are ready to give a shape to our React project. I set up the file and folder structure of my src folder as blueprinted below:

src
├── App.css
├── App.tsx
├── Map
│   ├── Map.scss
│   ├── Map.tsx
│   └── index.ts
├── index.css
├── index.tsx
├── react-app-env.d.ts
└── utils
└── GoogleMapsUtils.ts

Implementing loadMapApi() Utility

While developing a Google Maps component, we need to load the script tag which will download the API library for us. At the end, our webpage should show something like this:

<script 
src="https://maps.googleapis.com/maps/api/js?key=<YOUR_API_KEY>&libraries=places&language=no&region=NO&v=quarterly"
async defer>
</script>

Before loading this tag into our DOM structure, we cannot run Google Maps related code pieces. Therefore, we should create a utility function under utils/GoogleMapsUtils.ts.

Screenshot 2020-08-16 at 21.29.09
loadMapApi() Utility Function

This function basically creates the aforementioned script tag and inserts it into the DOM if it is not found. loadMapApi() will be used by the page which calls our Map component. In my example, this page will be the homepage of the project (App.tsx).

App.tsx

In this application, we have two fundamental tasks for App.tsx. First one will handle loading the map utility and latter will call our custom Map component. To do that, I created the React hook below:

const [scriptLoaded, setScriptLoaded] = useState(false);

This hook makes sure that Google Maps script tag is loaded successfully into the page. When scriptLoaded hook is set true, App.tsx renders my custom Map component.

useEffect(() => {
    const googleMapScript = loadMapApi();
    googleMapScript.addEventListener('load', function () {
        setScriptLoaded(true);
    });
}, []);

useEffect hook above runs when App component is mounted and when the script is loaded, we are ready to render Map component as shown below:

return (
    <div className="App">
        {scriptLoaded && (
            <Map
              mapType={google.maps.MapTypeId.ROADMAP}
              mapTypeControl={true}
            />
        )}
    </div>
);

Map/Map.tsx

We finally reached to the heart of the application. Map.tsx is my custom map component that I mentioned under the previous title. Let’s create its interface (IMap) first.

interface IMap {
    mapType: google.maps.MapTypeId;
    mapTypeControl?: boolean;
}

I wanted to receive two basic props. mapType can have ROADMAP, SATELLITE, TERRAIN or HYBRID values. These are specific map types defined by Google.

Screenshot 2020-08-16 at 22.04.24
google.maps.MapTypeId.ROADMAP

 

Screenshot 2020-08-16 at 22.06.29
google.maps.MapTypeId.SATELLITE

And we will use mapTypeControl prop to show/hide the control menu on the left-top corner of your map. We may extend the number of props to increase the usability of our map component, but I wanted to use only two props to illustrate my example.

I also use typescript’s custom type definition feature to increase the readability of my source code.

type GoogleLatLng = google.maps.LatLng;
type GoogleMap = google.maps.Map;

Before I explain the initMap, defaultMapStart, and startMap functions, I would like to show you the render part of the Map component.

const ref = useRef<HTMLDivElement>(null);

return (
<div className="map-container">
 <div ref={ref} className="map-container__map"></div>
</div>
);

The inner div element is the reference point for Google Maps. The API will load my map content into this div element and I use the outer div just for styling purposes. On the other hand, the same ref value is used by initMap function where the map is triggered.

Screenshot 2020-08-16 at 22.31.48
initMap function and map hook

As you see above, before implementing the initMap function, I created a hook called map. This hook is used to keep the map object created by google.maps.Map(…) function. The same function takes our div element via ref.current as its first parameter and the second parameter is a json object which keeps the preferences of our map.

initMap receives two inputs: zoomLevel and address. zoomLevel is straightforward to understand, but what address does will be more clear when we take a look at defaultMapStart() function.

Screenshot 2020-08-16 at 22.37.33
defaultMapStart function

So, this function basically calls the initMap, but it also does some critical work as well. It decides where to focus and how much to focus when our map starts.

Screenshot 2020-08-16 at 22.46.40
startMap function

Finally, startMap function calls defaultMapStart() when the map hook is not null anymore. This check is necessary because of rendering delays and typescript will complain if you do not run this check.

CONCLUSION

This application may not seem so impressive in terms of functionality, but you see that we had to do a lot of things just to start our map. While shooting the video, I had a problem with the API key because I did not want to screencast it via my youtube video, but when I include it, the output is as shown below:

Screenshot 2020-08-16 at 22.04.24
Final output

I am planning to screencast a second video where I will show how to add/remove markers on Google Maps. I hope you enjoyed this tutorial 🙂

 

Sercan Leylek / OSLO

Is it possible to trigger two DOM events at the same time on React?

Few weeks ago, I found myself desperately googling this question while I was trying to fix a React glitch. The answer is certainly, No! DOM is made to receive one user interaction event at a time, but I wanted to get rid of this error so badly and I was still hoping that there could be a way.

Let me clarify the case with a more elaborative example. I created a simple typeahead component which can be downloaded via https://github.com/SercanSercan/storksnest/tree/master/simple-typeahead

simple-typeahead-demo
Simple typeahead component demo

As the gif image above declares, there are two basic UI elements in a typical typeahead component: There should be a text box and a list of items that become visible upon user interaction. Component should also have some keyboard navigation where the glitch pops up.

Please take a look at the render part of the typeahead:

Screenshot 2020-05-24 at 18.40.08
Core render of typeahead component

Typeahead basically has the following DOM structure

  • div.typeahead => wrapper
    • input => textbox
    • ul => unordered list
      • li => list item to show a capital
      • li => list item to show a capital

As the code snippet above shows, when the user sets the focus on input, showSuggestions hook is set true. Hence, unordered list becomes visible. I did this by using onFocus and onBlur events. This is quite straightforward thinking. If the user tabs through my text box, dropdown will be shown to user and it will be hidden on the next tab. However, when the user clicks on a list item (to select a capital), onBlur got triggered although I wanted to trigger my onClick event on <li/>. Therefore, I quit using onClick event and used onMouseDown instead. Then, typeahead component worked like a charm 🙂 As a result, I couldn’t trigger two DOM events at the same time, but found a way to orchestrate related events. This should be the approach to solve such problems among UI events.

BONUS MATERIAL => Jøkul’s useKeyListener hookstorksnest_jøkul_bonus_useKeylistener_hook

Aforementioned application (simple-typeahead) can be inspiring for those who are about to develop their own dropdown component. At this point, I would like to introduce you a promising UI library called Jøkul. This library provides you stylish UI components (checkbox, datepicker, progressbar, etc. ) while creating your SPA. In my application, I benefited a custom hook from Jøkul. So, how to use Jøkul’s useKeyListener hook?

Step 1:

Install jkl-react-hooks on your web app:

npm i @fremtind/jkl-react-hooks

Step 2: 

Import useKeyListener under your target component:

import { useKeyListener } from '@fremtind/jkl-react-hooks';

Step 3: 

Identify the set of keys that you will use. In my case, I needed to use following list of keys:

const keys = ['ArrowUp', 'ArrowDown', 'Enter', 'Escape'];

Step 4: 

Create a reference for your input element:

const ref = useRef(null);
...
<input ref={ref} ...

Step 5:

Program it your way! As the code snippet below indicates, I created a function called onKeyPressed and sent it to Jøkul’s useKeyListener. Besides, my function consists of nothing but a switch case where I implement expected output. For example, if the user presses arrow down key, I increment the value of selectedIndex as long as it is within the length of my suggestions list.

Screenshot 2020-05-24 at 19.09.59

Jøkul’s useKeyListener stops you googling key codes and your source code becomes more readable and neat.

Sercan Leylek / OSLO

Generative Technology

During this year’s spring semester, I followed IT and Management course at University of Oslo. The course content involves IT platforms, how to bootstrap and govern them. While studying through academic network, Wikipedia becomes my best friend. However, Wikipedia or the rest of the internet does not have all the academic definitions for challenging terms. So, this question puzzled my mind for a while: What is generative technology?

Generative technology, as defined by Zittrain, is a technology that allows third parties to innovate upon it without any gate-keeping. The perfect example is the personal computer, which allows any type of third party to develop hardware or software to supplement or modify the original device. Further examples to generative technology are internet, Google Play and App Store.

GAN_Tech_Explained_Springwise

There is an interesting question at this point: Even though telephony system is over 100 years old, why did it fail to evolve itself as a generative technology like internet did? This is because of the dummy terminal architecture of telephony system. Internet is built on end-to-end principle which lets the nodes of the web to flow data to given address, but the end points of internet has smart devices (computers) which can be developed further. However, telephony system is developed to take care of the management problems via its core components, and its end points are dummy devices. As a result, although both internet and telephony system did not have big changes on their core architecture or how they work in last 50 years, telephony system did not manage to expand itself as a generative technology, but internet is used overall.

Internet has been the major generative technology invention of 20th Century. However, artificial intelligence will surely become the next big generative technology of 21st. Its ultimate power will be unlocked when computers can design and code infrastructures for complex systems.

Sercan Leylek / OSLO

ReferenceError: google is not defined

This error message bugged for two days. I’ve been working on a web application which is written in React+Typescript. My task is to create a Google Maps component without using any node module helper. So, I have to write most of the things from scratch and I had no former experience with Google Maps library. Before I tell more about my other concerns, let me introduce the error.

Description of the error

I experienced this error on Firefox meanwhile my web-app worked just fine on Chrome and Safari. When I tried to get predictions from Google Maps Api, Firefox showed me the following runtime error.

ReferenceError google is not defined firefox
“ReferenceError: google is not defined” occurs on Firefox

Solution

The solution is simple! While I was adding my Google Maps API script, I forgot to add async and defer attributes. After adding these attributes, my component did not crash on Firefox. There are two ways of doing this:

1. You may include these attributes inside your index.html

Screenshot 2020-04-30 at 16.27.44

2. Or you can add these values during Dynamic Loading process

ReferenceError google is not defined firefox solution

BONUS TIP

Do not add Google Maps API script into the head of your html. It should be at the bottom of your body 🙂

Sercan Leylek / OSLO

Squash Your Commits

When I heard this Git term for the first time from a colleague (who is English 💂), I thought it was a British idiom 🇬🇧. I even looked it up on urbandictionary.com, and I quickly realised that was an actual feature of Git. So, I had to teach this to myself. Then I googled “How to squash commits” and I ended up with a not very useful document on github. (https://github.com/wprig/wprig/wiki/How-to-squash-commits)

In very simple words, squashing your commits merges your several commits into a single commit. And there is no such “git squash blah blah” command either. We actually use “git rebase” command to achieve that. See the example case below to squash your commits 🙂

Example case

Assume that you have a development branch (called dev) and a feature branch (called my-pr-branch). The picture below lists my 4 commits which are on my-pr-branch.

how to squash commits sercan leylek

By following the steps below, I will squash those 4 commits into 1 commit.

STEP-1

Make sure that you are on feature branch:

git checkout my-pr-branch

STEP-2

Run the command below:

git rebase -i HEAD~4

In my case, I will squash 4 commits. Therefore, I rebase HEAD~4.

STEP-3

Previous step will open your default text editor. See my case below:

Screenshot 2019-06-29 at 18.04.27

STEP-4

Leave first line as it is and type ‘s’ for the first word of the other lines. As the comments also elaborate, ‘s’ stands for squash.

Screenshot 2019-06-29 at 18.05.51

STEP-5

After you save the editor (:wq), you will be demanded to save the merge request. Just save and move forward:

Screenshot 2019-06-29 at 18.10.04

FINAL STEP

Push your changes with the command below:

git push origin my-pr-branch --force

Squash commits
Result

And the result will be one commit to rule them all, one commit to find them.

giphy

Sercan Leylek / OSLO

How to resend SMS verification in Firebase Phone Authentication?

We all know that feeling. You don’t have a concrete idea about the solution you are looking for and you want to see a quick answer for your question. So, check the short answer below:

Short answer to your question

Very short answer is a “NO”. Firebase PhoneAuthProvider does not deliver a resend SMS feature in its SDK for JavaScript developers (as of now, June-2019). It actually supports such feature for Android, C++ and Unity developers via PhoneAuthProvider.ForceResendingToken, but the same is not available for JavaScript developers. Therefore, if you’d like to create a “Resend SMS Verification” button for your web application, you should implement it yourself by initialising a new recaptcha session (like setting window.recaptchaVerifier etc…), using a separate recaptcha container (such as <div id=”recaptcha-container-resend” />) and eventually calling signInWithPhoneNumber(…) function again on user interaction.

Long answer to your question

The screenshot below shows you the documentation of Firebase services in various platforms. You may access the Javascript SDK reference by clicking the link below:

https://firebase.google.com/docs/reference/js

Screenshot 2019-06-16 at 17.12.00

To implement a resend SMS feature on my ReactJS app, I went through this documentation. And I realised that there wasn’t any resend SMS support by Firebase.auth. Interestingly, there was no blog or stackoverflow ticket discussing the issue either. Then, I assumed that the developers, who came across with the same problem, silently implemented a resend SMS feature by simply re-initialising the whole process on their web applications. Therefore, I decided to break this silence by writing this post and helping future developers (and maybe even future self 🙂 ).

Assume that your application shows two components to the user. First component will receive the phone number from the user and after the SMS is sent, the second component will demand the user to type in the verification code.

Your first component should run following methods in order:

  1. Initialize recaptcha
    • window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier(…);
  2. Sign in with given phone number
    • auth.signInWithPhoneNumber(phoneNumber, appVerifier);
  3. Reset or even remove recaptcha
    • grecaptcha.reset(window.recaptchaWidgetId);

Above is a rough sequence of the operations that you should follow and I believe you already know how these work.

When your application moves to the second component, you may wish to use the existing implementation (the functions in the first component) by passing the functions as arguments in React. However, this approach creates some trouble for the RecaptchaVerifier, because you already used the recaptcha session to send the first SMS and if your user attempts to send the SMS again, you will need a new recaptcha session. Plus, you will also need a recaptcha container in your second component as well. Recaptcha container is the place that grecaptcha-badge will be loaded in an iframe. In order to avoid these conflicts, I have implemented the same set of methods in the second component as well. And to prevent any other potential conflicts, I created a separate recaptcha-container with a new id.

First component’s container:

<div id=”recaptcha-container” />

Second component’s container:

<div id=”recaptcha-container-resend” />

So that, the user may resend SMS verification several times more as long as RecaptchaVerifier does not believe that the user is a robot 🙂

Other Tips

Always use forms

See the screenshot below from my web application. The user interface is in Norwegian 🇳🇴 🙂

Screenshot 2019-06-16 at 17.49.23
Example User Interface for SMS Verification

We basically have one text box for the phone number and a submit button. So, this has to be implemented as a form, right? The picture below shows a simplified JSX code for this form:

Screenshot 2019-06-16 at 18.06.11

A good practice is to place the recaptcha-container at the bottom of the form. So that, there will be no confusing accessibility issues for the blind people. Also, we initialise the sign in process via this.handleSubmit call. This approach provides an advantage for desktop users, because handleSubmit will allow triggering the recaptcha check by pressing [ENTER] button while the focus is on phoneNumberInput element (<input … />). As a result of these benefits, always use forms while implementing Firebase SMS Verification interface.

Watch out for unknown error states

PhoneAuthProvider gives you a list of error codes, but there is no documentation for all errors. (See documentation here.) One of these obscure errors is auth/code-expired. This error is triggered when the user enters the wrong SMS code three consecutive times.

Recaptcha window is not responsive

This is another unfortunate case with Firebase Recaptcha services. Captcha pictures presented inside the iframe have fixed width and critical recaptcha buttons become invisible and non-scrollable for the users with very small mobile screens (such as iPhone5). To fix this issue, I recommend using some media query on grecaptcha-badge CSS class. See the code below:

Screenshot 2019-06-16 at 18.16.29
Media query for

The result is not perfect, but it keeps the iPhone 5 users in the game.

IMG_1793

Use libphonenumber-js for formatting

If you’d like to format the phone number inside the text box (<input className=”phoneNumberInput” … />), libphonenumber-js is a great utility. You can apply required country code settings by simply adding 3 lines of code into your application. You can learn more about AsYouType formatter by the URL below:

https://www.npmjs.com/package/libphonenumber-js

Credits to my colleague Iva Ivanova 👩‍🔧 on this final tip.

That’s all folks! See you next time with another challenge!

Sercan Leylek / OSLO

 

 

Using Cloud Firestore with React

Cloud Firestore is a NoSql database and this is my first time in Firebase world’s schema-less databases. This article should help to those who would like to get a grip on NoSql databases and who would also like to practice React programming further.

If you have no idea about NoSql databases, I recommend you to watch the video below:

What do we build in this tutorial?

Let’s have a vision of what we are actually developing here. The name of my application is show-cities-firebase and the whole source code is in my github repository. Besides, the app will have following features:

  • Connecting React app with Cloud Firestore database (Not Realtime Database)
  • Keep three attributes for each record
    • ID
    • City name
    • Population
  • List city names and corresponding population data
  • Sort the list by population
  • Delete specific city record
  • Delete all cities

Cloud Firestore Configuration

To start with, you need to include Firebase console access to your regular Google Account (same as gmail account). You may try to get it via https://console.firebase.google.com. And the same console address will help you follow your storage while we are working further. In other words, you will see your data collections under the console. See the GIF animation below to understand what I mean.

console_firebase_collections_example
Checking out Cloud Firestore Collections on console

The next thing you should do is to set up your config input. You must fill in the content regarding your account credentials. I keep this info inside my fire.js file.

Screen Shot 2019-04-22 at 11.57.45
fire.js

And I eventually import the configuration file inside my App.js:

import fire from './fire';

As you see in the picture above, you also need database.rules.json and firebase.json files. Add them to your project as I did in github repository. And we are ready to pull and push data.

Insert City Data

See how the constructor code looks like.

Screen Shot 2019-04-22 at 12.06.06

I keep the cities as an array inside of the state and the cities array contains the required data fields such as id, city and population. I think sortASC attribute’s task is apparent and I initialise the app with maxID = -1 value. Firebase databases do not provide auto-increment feature like in MySql or Oracle databases. Instead, you should do this manually. Therefore, I need maxID to auto-increment ID value for new city records. When the app starts, this value is updated with the highest ID. I call the function below on componentDidMount().

Screen Shot 2019-04-22 at 12.11.18

updateMaxID will return -1 unless there is some data in my collection. However, if there are earlier city records, it sorts the collections in descending order and picks the first top one thanks to the line below:

let maxIdItem = dbCollection.orderBy("id", "desc").limit(1);

After explaining how I set up my application state, let’s see how to insert some data into our collection. Take a look at my render code:

Screen Shot 2019-04-22 at 12.17.02

The application basically has two textboxes – city and population. I use required keyword for data validation, because I do not want any record either without a city name nor population. In addition to that, we have a tiny button which triggers this.addCity() on form submit.

Screen Shot 2019-04-22 at 19.27.03

This is the function where we implement the auto-increment of ID. I refer to this.state.maxID and increment it by 1. After inserting my new record into Cloud Firestore, I call this.updateMaxID().

You must have noticed that population and city ID values are parsed to integer before sending them into the database. This is necessary for sorting implementations and if you do not specify the type of the data, Firebase will store those values as String. Moreover, unlike relational databases, Firebase does not apply strict type rules. Therefore, one record may store the population value as String and while the next one stores it as integer. This is legal in NoSql world 🙂

In the last two lines of this.addCity(), I simply reset the textboxes with empty string. See the sample use below:

react_firebase_city_population_tutorial
Sample output

react_firebase_validation_required_example
Validation example

Listing records

Since we are able to insert some records into our collection, we had better list them as well. To do that, I wrote a function called this.loadCities() which pulls the data into application state.

Screen Shot 2019-04-22 at 20.03.32

The function above does not render the records. Therefore, I wrote another function which lists the city data and it also handles the sorting operations – this.createCitiesTable().

I will skip the implementation of other features. You may try to implement the rest on your own as a homework. If you get stuck, the github repository is always at your service.

Sercan Leylek / OSLO

Learning React – Vol.2

Last week, I went through the solutions of the first three tasks at tic-tac-toe game tutorial. This time, I will cover up the rest. Please see the list of tasks again:

  1. Display the location for each move in the format (col, row) in the move history list.
  2. Bold the currently selected item in the move list.
  3. Rewrite Board to use two loops to make the squares instead of hardcoding them.
  4. Add a toggle button that lets you sort the moves in either ascending or descending order.
  5. When someone wins, highlight the three squares that caused the win.
  6. When no one wins, display a message about the result being a draw.

If you’d like to take a look at the initial article, please check this one out.

Task #4 Add a toggle button to sort the moves

I must admit that I struggled with this task a bit, because I couldn’t make my mind whether I should reverse the history in this.state or in a copy of history array. After my first implementation, I realised that things get fuzzy when you mingle with the state quite often. Because, making changes in the state side-effects the other functions inside Game.js. So, I threw away my first code and followed a brand new plan.

To start with, we definitely need a new state attribute for the sorting choice. I added it to my constructor with default value ascending.

Screen Shot 2019-02-28 at 23.46.28.png
Add this.state.sortASC to the constructor of Game.js

 

We surely need a new function to toggle the sorting direction. This function will be called each time the user clicks on our sort button. So, I added toggleSort() inside Game component.

Screen Shot 2019-02-28 at 23.47.59.png
Add toggleSort() function

It is time to make some changes at rendering now. Since I do not want to reverse this.state.history, I get a copy of history and reverse it if sorting direction is descending. Also, please note that I also reverse the move value inside map(step, move).

Screen Shot 2019-02-28 at 23.52.44.png
Get a copy of history and reverse the copy

Next change inside the render creates our toggle button. I have been too lazy to add some SVG images and work on CSS. Therefore, I used UTF-8 arrows 🙂 And do you remember the toggleSort() function? This is a good time to use it.

Screen Shot 2019-02-28 at 23.55.33
Implement sortButton with an arrow

The last snippet is a piece of cake.

Screen Shot 2019-03-01 at 00.00.24
Add sortButton and use unordered list if you like

Ta taaa!!! Below is the outcome. If you’d like to see the commit, click here.

toggle sort tic-tac-toe react game
Tic-tac-toe game with sorting

Task #5 When someone wins, highlight the three squares that caused the win.

As we go further, the tasks become a bit more challenging. So, this implementation requires changes on all components. I will follow bottom to top approach. That means we will see the implementation sequence as following:

  1. Square.js
  2. Board.js
  3. Game.js

Square component is called by Board and it is responsible of rendering the boxes. Assume that Square.js receives an additional prop parameter which is boolean. And this input helps rendering the given box with gray background color or not. See the code below:

Screen Shot 2019-03-02 at 13.53.24
Add winnerSquare className if the boolean is true

And we certainly make this minor change inside index.css.

Screen Shot 2019-03-02 at 13.58.52.png
index.css

After these changes, we are done with the Square component. The next stop is Board. Let us first establish our previous assumption for the Square. Consider that renderSquare() function will pass the boolean to the Square.

Screen Shot 2019-03-02 at 14.00.53
renderSquare should get a second input: winnerSquare

And our createTheBoard() function still calls the previous function inside two for-loops. Assume that we have a handy function which figures out if the current square box is a winner box. So, call it accordingly.

Screen Shot 2019-03-02 at 14.05.18
this.isWinnerSquare(arrayPos)

And it is time to keep our promises again. Implement isWinnerSquare(). But see that we again make a new assumption here. Assume that Board will receive the winner coordinates from Game.js. If the coordinates are null, boolean is false. If current square box position is one of the winners, boolean is true. Simple huh?

“If you can’t write it down in English, you can’t code it.” – Peter Halpern

Screen Shot 2019-03-02 at 14.06.37
Implement isWinnerSquare(…)

We are about to finalise the implementation. calculateWinner(…) knows which lines are the winner squares. Instead of returning the name of the winner, it’d better start sending an object which keeps both the coordinates and the winner name.

Screen Shot 2019-03-02 at 14.12.29
calculateWinner will send the coordinates and the winner name.

As the final touch, I edited the use of winner object and the result is astonishing 🙂 See the commit here.

When someone wins, highlight the three squares that caused the win
When someone wins, highlight the three squares that caused the win.

Task #6 When no one wins, display a message about the result being a draw.

The last task is kind of easy. I see that the draw happens when all squares have some X or O value, but the calculateWinner function yet does not return a winner. So, I insert my check inside the if scope:

Screen Shot 2019-03-02 at 14.44.33
Game is a draw

If you are curious about the commit, check it here. And below is the final output of our tic-tac-toe game:

When no one wins, display a message about the result being a draw
Enhanced version of tic-tac-toe game

As you see, the winner of the last game is neither X or O, because the real winner is YOU who puts effort to learn React.

Sercan Leylek / OSLO