DEV Community

Cover image for Advanced JavaScript Concepts
Luc van Kerkvoort
Luc van Kerkvoort

Posted on

Advanced JavaScript Concepts

I was sitting behind my computer thinking of creating a small application that would leverage some advanced JavaScript concepts.. in particular I was thinking of real world implementations of throttle and debounce.

I've created an application that kind of leverages the logic of throttle but will also showcase some difficult to maneuver web api's such as setTimeout and setInterval

if this has piqued your interest.. please read on.

TLDR

for those of you that don't like to read.. here is the logic for the application

const GetScores = () => {
  const [homeScore, setHomeScore] = useState(0);
  const [awayScore, setAwayScore] = useState(0);

  const updateScore = () => {
    const scoreOptions = [0, 2, 3];
    const randomNum = () => Math.floor(Math.random() * 3);

    setHomeScore(homeScore + scoreOptions[randomNum()]);
    setAwayScore(awayScore + scoreOptions[randomNum()]);
  };

  return {
    homeScore,
    awayScore,
    updateScore,
  };
};



const usePlayGame = ({ 
  team1 = "https://cdn.mos.cms.futurecdn.net/8227s5kMEx84YEHWLbxCb4-1200-80.jpg",
  team2 = "https://www.dailynews.com/wp-content/uploads/migration/2015/201506/SPORTS_150619439_AR_0_FHMRLLUSDJPX.jpg?w=1024",
  timeLapse,
  delay,
}) => {
  const [time, setTime] = useState(15);
  const [pause, setPause] = useState(false);
  const [quarter, setQuarter] = useState(0);
  const [winner, setWinner] = useState("");

  const { updateScore, homeScore, awayScore, homeTeam, awayTeam } = GetScores(
    team1,
    team2
  );

  useEffect(() => {
    // if we have finished the last quarter
    if (quarter >= 3) {
      // we decide the winner by checking the scores
      const winner = homeScore > awayScore ? homeTeam : awayTeam;
      //   we set the winner
      setWinner(winner);
      //   we pause the game indefinitely
      return setPause(true);
    }
    // Let's unpause after a break (setTimeout callback)
    if (pause) {
      setPause(false);
    }

    // on every interval increase time by the timelapse set (default 1000 ms)
    const interval = setInterval(() => {
      updateScore();
      setTime(time - 1);
    }, timeLapse);

    // if the time is run out and the game is not yet over
    if (time === 0 && quarter !== 3) {
      // We pause the match
      setPause(true);
      //   we move to the next quarter
      setQuarter(quarter + 1);
      //   we reset the time after a short break (delay default 5000 ms)
      setTimeout(() => {
        setTime(time + 15);
      }, delay);
      return clearInterval(interval);
    }
    return () => clearInterval(interval);
  }, [time]);

  return {
    time,
    pause,
    quarter,
    winner,
    homeScore,
    awayScore,
    homeTeam,
    awayTeam,
  };
};

Enter fullscreen mode Exit fullscreen mode

Fantasy Basketball

for the implementation of the logic I wanted to create something fun. Something that I've started enjoying a lot... Basketball. Go Warriors!! am I right.

I have decided to create a fantasy basketball application that can run an entire game as soon as the page loads. There are a lot of moving parts to this application, but not to worry. we will build out every piece individually. The following we will need:

  • a timer
  • a scoreboard
  • breaks after every quarter
  • deciding a winner

Prerequisites

This tutorial will be build in React with JavaScript, a basic understanding of both would definitely come in handy for this tutorial just to understand some of the more complex pieces of the application.

we will be installing one more dependency.. styled components.

npm install --save-dev styled-components

not a necessity, just very powerful in my opinion and comes in handy in almost every project. It is a library to turn your css into React components where you can also pass along properties to it to manipulate the css.

Getting started

to get started with any react project for home use, we can simple use Create React App (CRA). Create React app is a powerful tool to bootstrap a React project and the perfect solution for building simple applications like this.

Run the following command to get started.

npx create-react-app ${your app name here}

then type the following command into your CLI cd ${your app name here} and voila. we have a project setup.

Structure

Below you can see the structure of the game.
I will be going over every component as we move along.

Structure

Recreate the structure as you see fit, if you want to follow along with me then recreate the folders as shown in the image above

API

the first component I would like to build is the API. In this tutorial I'm not actually using an API, I have just created a hook that runs locally but basically mocks an API call.

this logic is stored in src/Services/api.js

first let's start with what we want this API to return.
We want the following:

  • Home team score
  • Away team score
  • function to update the score
import { useState } from "react";

export const GetScores = (
) => {
  const [homeScore, setHomeScore] = useState(0);
  const [awayScore, setAwayScore] = useState(0);

const updateScore = () => {
    const scoreOptions = [0, 2, 3];
    const randomNum = () => Math.floor(Math.random() * 3);

    setHomeScore(homeScore + scoreOptions[randomNum()]);
    setAwayScore(awayScore + scoreOptions[randomNum()]);
  };

  return {
    homeScore,
    awayScore,
    updateScore,
  };
};

Enter fullscreen mode Exit fullscreen mode

to keep track of the scores I created to state variables that can keep track of each teams scores independently.

Next I needed a function that would update the score whenever we needed it to. The updateScore function does just that. Every second we run the play, the updateScore function runs on each of the teams.

I've created an array with the possible scores in a basketball game being: 0, 2 and 3. You can dunk, layup or shoot to get 2 points and you can shoot from the 3 point line to get 3 points, your only other option is missing.

I then needed something to determine which one of the three options it would be. I created a function that returns Math.floor(Math.random()*3). Let's break that down.

Math.floor takes out decimal values and "floors" them so brings it basically removes the decimals (e.g. 10.8 is 10)

Math.random will give us a random numerical value between 0 and 1 (e.g 0.245 or 0.678) we multiply this with the amount of options we have (3) and we will receive a random number between 0 and 2 which corresponds with the indexes in our array.

then we set the new score by adding up the old score with the randomly selected outcome and return those.

Hooks

Now it is time to get to the nitty gritty. I've created a custom hook to take care of all the logic pertaining to the actual game. This way I keep my UI component clean of too much logic and make it easier to read.

this logic is stored in src/Services/hooks.js


import { useState, useEffect } from "react";
import { GetScores } from "./api";

const usePlayGame = ({ team1, team2, timeLapse = 1000, delay = 5000 }) => {
  const [time, setTime] = useState(15);
  const [pause, setPause] = useState(false);
  const [quarter, setQuarter] = useState(0);
  const [winner, setWinner] = useState("");

  const { updateScore, homeScore, awayScore } = GetScores();

  useEffect(() => {
    // if we have finished the last quarter
    if (quarter >= 3) {
      // we decide the winner by checking the scores
      const winner = homeScore > awayScore ? team1 : team2;
      //   we set the winner
      setWinner(winner);
      //   we pause the game indefinitely
      return setPause(true);
    }
    // Let's unpause after a break (setTimeout callback)
    if (pause) {
      setPause(false);
    }

    // on every interval increase time by the timelapse set (default 1000 ms)
    const interval = setInterval(() => {
      updateScore();
      setTime(time - 1);
    }, timeLapse);

    // if the time is run out and the game is not yet over
    if (time === 0 && quarter !== 3) {
      // We pause the match
      setPause(true);
      //   we move to the next quarter
      setQuarter(quarter + 1);
      //   we reset the time after a short break (delay default 5000 ms)
      setTimeout(() => {
        setTime(time + 15);
      }, delay);
      return clearInterval(interval);
    }
    return () => clearInterval(interval);
  }, [time]);

  return {
    time,
    pause,
    quarter,
    winner,
    homeScore,
    awayScore,
  };
};

export default usePlayGame;
Enter fullscreen mode Exit fullscreen mode

Let me break this badboy down. Let's start with the useEffect and I'll break down what each part of the hook will do.



    // if we have finished the last quarter
if (quarter >= 3) {
      // we decide the winner by checking the scores
      const winner = homeScore > awayScore ? team1 : team2;
      //   we set the winner
      setWinner(winner);
      //   we pause the game indefinitely
      return setPause(true);
    }

Enter fullscreen mode Exit fullscreen mode

This if statement is basically a principal taken from recursive programming. When we reach the final quarter we want the useEffect to no longer run so we return with setPause to true.

Recursive programming is where we call a function inside of itself and have an exit after we have reached a certain point.

let count = 0;

const addTo10 = () => {
if(count === 10) return

console.log(count)

addTo10(count++)

}
Enter fullscreen mode Exit fullscreen mode

as you can see with this simple example, we have a count that starts off at 0. The addTo10 function calls itself with count++ adding up the count every time it runs. The if statement in the beginning makes sure that when the count equals 10 we return and the function stops running.

you can copy and paste this into a browser to see how it works.

the next code block that is important is this

// on every interval increase time by the timelapse set (default 1000 ms)
    const interval = setInterval(() => {
      updateScore();
      setTime(time - 1);
    }, timeLapse);

Enter fullscreen mode Exit fullscreen mode

we create a timer by using setInterval which reruns a function after every interval. The timeLapse variable is set to 1000ms in the above example which accounts for a single second. so we decrease a number value by 1 every time it runs. This is our clock.

on the interval we update the score which is the function we get from our API we created before:


  const { updateScore, homeScore, awayScore } = GetScores();
Enter fullscreen mode Exit fullscreen mode

this will update the score and export the homeScore and awayScore for the respective teams.

 // if the time is run out and the game is not yet over
    if (time === 0 && quarter !== 3) {
      // We pause the match
      setPause(true);
      //   we move to the next quarter
      setQuarter(quarter + 1);
      //   we reset the time after a short break (delay default 5000 ms)
      setTimeout(() => {
        setTime(time + 15);
      }, delay);
      return clearInterval(interval);
    }
    return () => clearInterval(interval);
  }, [time]);

Enter fullscreen mode Exit fullscreen mode

this is the main code. Let's go through it step by step. if the time hits zero and it isn't the final quarter we want to set pause to true, increase the quarter and use a setTimeout callback to reset the timer back to 15 and use a delay variable to set the milliseconds we want to wait (default 5000 ms or 5 sec). within useEffect we will have to return the clearInterval to stop the intervals from running.

after this has run we will have to setPause to false to continue our loop

if(pause){
setPause(false)
}
Enter fullscreen mode Exit fullscreen mode

Throttle

if you look up the principle of throttle in JavaScript it is usually has similar code as the one above. we want to set a variable or state to true so a function can run, inside the function we set the state to false to make it stop running and use a setTimeout to start the function again. In this case setInterval is taking care of resetting the state.

below you can see an example of throttling in JavaScript

//initialize throttlePause variable outside throttle function
let throttlePause;

const throttle = (callback, time) => {
  //don't run the function if throttlePause is true
  if (throttlePause) return;

  //set throttlePause to true after the if condition. This allows the function to be run once
  throttlePause = true;

  //setTimeout runs the callback within the specified time
  setTimeout(() => {
    callback();

    //throttlePause is set to false once the function has been called, allowing the throttle function to loop
    throttlePause = false;
  }, time);
};

Enter fullscreen mode Exit fullscreen mode

UI

now for the fun part. We can build out the UI. The UI is build out into several components, the main one being ScoreCard

import React from "react";
import Winner from "../Winner";
import { CardWrapper, Score, Teams, TeamLogo } from "./styles";
import usePlayGame from "../../Services/hooks";
import Time from "../Time";
import Break from "../Break";
import { game } from "./utils";

const ScoreCard = ({
  team1 = "https://cdn.mos.cms.futurecdn.net/8227s5kMEx84YEHWLbxCb4-1200-80.jpg",
  team2 = "https://www.dailynews.com/wp-content/uploads/migration/2015/201506/SPORTS_150619439_AR_0_FHMRLLUSDJPX.jpg?w=1024",
  timeLapse,
  delay,
}) => {
  const { quarter, time, homeScore, awayScore, pause, winner } = usePlayGame({
    team1,
    team2,
    timeLapse,
    delay,
  });

  return (
    <CardWrapper>
      <Teams>
        <TeamLogo src={team1} /> vs
        <TeamLogo src={team2} />
      </Teams>
      {game[quarter]}
      <Score>
        {homeScore} - {awayScore}
      </Score>
      {pause ? <Break quarter={quarter} /> : <Time time={time} />}
      <br />
      {winner && <Winner img={winner} />}
    </CardWrapper>
  );
};

export default ScoreCard;


Enter fullscreen mode Exit fullscreen mode

The scorecard has the teams

<Teams>
 <TeamLogo src={team1} /> vs
 <TeamLogo src={team2} />
</Teams>
Enter fullscreen mode Exit fullscreen mode

the quarter we are in which utilizes a utils.js file. I've combined both in the section below

{game[quarter]}

export const game = ["First Half", "Second Half", "Third Half", "Full Time"];
Enter fullscreen mode Exit fullscreen mode

it selects the correct game by using the quarter which runs from 0 to 3 to decide what part of the game we are in.

the next part is the score

<Score>
  {homeScore} - {awayScore}
</Score>
Enter fullscreen mode Exit fullscreen mode

which receives the homeScore and awayScore from our hooks.js file

then we have a couple of operators that use the Break, Time and Winner components

{pause ? <Break quarter={quarter} /> : <Time time={time} />}
<br />
{winner && <Winner img={winner} />}
Enter fullscreen mode Exit fullscreen mode

if we are on a pause we want to showcase the word 'break' except for when we reach full time. otherwise we want to showcase the time but because time runs from 15 to 0, we want to showcase it as 15, 14, 13, 12, 11, 10, 09, 08, 07, 06, 05, 04, 03, 02, 01, 00.

for those reasons I've build out the following components in the Break folder and the Time folder respectively.


//Break
import React from "react";

const Break = ({ quarter }) => {
  console.log(quarter);
  return <div>{quarter < 3 ? "Break" : ""}</div>;
};

export default Break;

//Time
import React from "react";

const Time = ({ time }) => <div>{time < 10 ? `0${time}` : time}</div>;

export default Time;

Enter fullscreen mode Exit fullscreen mode

the final piece of the puzzle is the Winner component


import React from "react";
import { WinnerCard } from "./styles";

const Winner = ({ img }) => {
  return (
    <div>
      <h1>!! Winner !!</h1>
      <WinnerCard src={img} />
    </div>
  );
};

export default Winner;

Enter fullscreen mode Exit fullscreen mode

Styled components

for the ScoreCard component and the Winner component I have created the following styles using styled components:

//ScoreCard
import styled from "styled-components";

export const CardWrapper = styled.div`
  display: flex;
  flex-direction: column;
  text-align: center;
`;

export const Score = styled.div`
  display: flex;
  font-size: 8rem;
  justify-content: center;
`;

export const Teams = styled.div`
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 3rem;
  font-weight: bold;
  gap: 1rem;
  padding: 15px;
  border-bottom: 10px double black;
`;

export const TeamLogo = styled.img`
  width: 200px;
  height: 115px;
  border-radius: 15px;
  padding: 10px;
  border: 10px double black;
`;

export const Winner = styled.div`
  display: flex;
  flex-direction: column;
`;

Enter fullscreen mode Exit fullscreen mode
//Winner

import styled from "styled-components";

export const WinnerCard = styled.img`
  width: 200px;
  height: 115px;
  padding: 10px;
  border: 5px double black;
  border-image: linear-gradient(to top right, #a67c00 10%, #fabf00 90%) 1 round;
`;

Enter fullscreen mode Exit fullscreen mode

these you can import into your file with named exports from './styles' like so

//ScoreCard
import { CardWrapper, Score, Teams, TeamLogo } from "./styles";

//Winner
import { WinnerCard } from "./styles";
Enter fullscreen mode Exit fullscreen mode

Extra Curricular

for those of you interested in the power of styled components I have added a styled component that I have used on my App.js to set the layout for the page


import styled from "styled-components";

export const Direction = styled.div`
  ${({ direction }) => {
    return direction === "row"
      ? `{
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  margin: auto 0;
  overflow: scroll;
  max-width: 100vw;
}`
      : `{
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  grid-gap: 1rem;
  overflow: scroll;
}`;
  }}
`;


Enter fullscreen mode Exit fullscreen mode

this component takes in a direction, if that direction is 'row' then the first part of the ternary is used on our wrapper component to set all of our child components in a single row. If you decide not to use that but go for 'column' or anything else for that matter instead, the flow will be done in a grid layout that is responsive.

App.js

Below you will find my implementation of the ScoreCard in our App.js file.

import { Direction } from "./styles";
import ScoreCard from "./Components/ScoreCard";

function App() {
  return (
    <Direction direction="row">
      <ScoreCard />
    </Direction>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Thank You

I hope this helped make sense of setInterval, setTimeout, throttle and recursion. It was a lot of fun to build and if you guys have questions I would love to hear them from you.

Please leave a like ❤️, leave a comment 📃 or connect 👋 :

Github
Linkedin
Discord

Here is a link to the Github Repo: Sports App

Top comments (4)

Collapse
 
orimdominic profile image
Orim Dominic Adah

It would be nice if you added the link to the deployed application and the project repo on GitHub.

Nice 👍

Collapse
 
lucvankerkvoort profile image
Luc van Kerkvoort

Just added it at the bottom of the article

Collapse
 
kaviiiiisha profile image
Kavisha Nethmini

@geesilu 🤩

Collapse
 
geesilu profile image
Luthira Geesilu

wow🤩