<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Mihail Gaberov - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Mihail Gaberov - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 16 May 2026 16:28:57 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/mihailgaberov/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Dynamic Web Scraper App with Playwright and React: A Step-by-Step Guide ]]>
                </title>
                <description>
                    <![CDATA[ Today we are going to build a small web scraper app. This application will scrape data from the Airbnb website and display it in a nice grid view. We will also add a Refresh button that will be able to trigger a new scraping round and update the resu... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-dynamic-web-scraper-app-with-playwright-and-react/</link>
                <guid isPermaLink="false">6787cb727ee491a35577e9d5</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webscraping  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ playwright ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Wed, 15 Jan 2025 14:51:30 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1736952641440/0aa6255b-45eb-4ae8-b5cb-d87648590e18.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Today we are going to build a small web scraper app. This application will scrape data from the Airbnb website and display it in a nice grid view. We will also add a Refresh button that will be able to trigger a new scraping round and update the results.</p>
<p>In order to make our app a bit more performant, we will utilize the browser’s local storage to store already scraped data so that we don’t trigger new scraping requests every time the browser is refreshed.</p>
<p>Here’s what it will look like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736256647942/56c176ad-2615-478a-94f8-e33b3b437b92.png" alt="Web Scrapper app interface" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-how-to-spin-up-the-app-with-vite">How to Spin Up the App with Vite</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-server">How to Build the Server</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-front-end">How to Build the Front End</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-heading-how-to-deploy-to-rendercom">How to Deploy to</a> <a target="_blank" href="http://render.com">render.com</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<p>If you want to jump straight into the code, <a target="_blank" href="https://github.com/mihailgaberov/web-scraper">here</a> <a target="_blank" href="https://orderbook-mihailgaberov.vercel.app/">💁</a> is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/web-scraper/blob/main/README.md">README 🙌,</a> and <a target="_blank" href="https://scraper-fe.onrender.com/">here</a> you can see the live demo.</p>
<p>Now if you’re ready, let’s go step by step and see how to build and the deploy the app.</p>
<p>First, let’s get everything set up and ready to go.</p>
<h2 id="heading-how-to-spin-up-the-app-with-vite">How to Spin Up the App with Vite</h2>
<p>We will use the <a target="_blank" href="https://vite.dev/">Vite</a> build tool to quickly spin up a bare bones React application, equipped with TailwindCSS for styling. In order to do that, run this in your terminal app:</p>
<pre><code class="lang-bash">npm create vite@latest web-scraper -- --template react
</code></pre>
<p>And then install and configure TailwindCSS as follows:</p>
<pre><code class="lang-bash">npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
</code></pre>
<p>Add the paths to all of your template files in your <code>tailwind.config.js</code> file like this:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [
    <span class="hljs-string">"./index.html"</span>,
    <span class="hljs-string">"./src/**/*.{js,ts,jsx,tsx}"</span>,
  ],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
}
</code></pre>
<p>Now you should have a brand new React application with Tailwind installed and configured.</p>
<p>Let’s start our work with the server.</p>
<h2 id="heading-how-to-build-the-server">How to Build the Server</h2>
<p>Since we are building a full stack application, the bare minimum we need to have in place is a server, a client, and an API. The API will live in the server world and the client app will call the endpoints it exposes in order to fetch the data we need to display on the front end.</p>
<h3 id="heading-set-up-the-http-server-with-expressjs">Set Up the HTTP Server with Express.js</h3>
<p>We are going to use the Express.js library to spin up an HTTP server that will handle our API requests. To do so, follow these steps:</p>
<p>First, install the necessary packages with this command:</p>
<pre><code class="lang-bash">npm install express cors playwright
</code></pre>
<p>Then create an empty <code>server.js</code> file in the project's root folder and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> { chromium } <span class="hljs-keyword">from</span> <span class="hljs-string">"playwright"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;
<span class="hljs-keyword">import</span> { scrapeListings } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/scraper.js"</span>;

<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> PORT = <span class="hljs-number">5001</span>;

app.use(cors());

app.get(<span class="hljs-string">"/scrape"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">let</span> browser;
  <span class="hljs-keyword">try</span> {
    browser = <span class="hljs-keyword">await</span> chromium.launch();
    <span class="hljs-keyword">const</span> listings = <span class="hljs-keyword">await</span> scrapeListings({ browser, <span class="hljs-attr">retryCount</span>: <span class="hljs-number">3</span> });
    res.json(listings);
  } <span class="hljs-keyword">catch</span> (error) {
    res.status(<span class="hljs-number">500</span>).json({ <span class="hljs-attr">error</span>: error.message });
  }
});

app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Scraper server running on http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<p>Before we continue with the scraper, let me first explain what we are doing here.</p>
<p>This is a pretty simple setup of an Express server that is exposing an endpoint called “scrape“. Our client-side application (the front end) can send GET requests to this endpoint and receive the data returned as a result.</p>
<p>What’s important here is the async callback function that we pass to the <code>app.get</code> method. This is where we call our scraping function within a try/catch block. It will return the scraped data or an error if something goes wrong.</p>
<p>The last few lines indicate that our server will listen on the specified PORT, which is set to 5001 here, and display a message in the terminal to show that the server is running.</p>
<h3 id="heading-what-is-web-scraping">What is Web Scraping?</h3>
<p>Before diving into the code, I want to briefly explain web scraping if you’re unfamiliar with it. Web scraping involves automatically reading content from websites using a piece of software. This software is called a “web scraper“. In our case, the scraper is what’s in the <code>scrapeListing</code> function.</p>
<p>An essential part of the scraping process is finding something in the DOM tree of the target website that you can use to select the data we want to scrape. This something is known as a <strong>selector</strong>. Selectors can be different HTML elements, such as tags (h3, p, table) or attributes, like class names or IDs.</p>
<p>You can use various programming techniques or features of the programming language you’re using to create the scraper, aiming for better results when implementing the selecting part of the scraper.</p>
<p>In our case, we’re using <code>[itemprop="itemListElement"]</code> as the selector. But you might wonder, how did we figure this out and decide to use that? How do you know which selector to use?</p>
<p>This is where it gets tricky. You have to manually inspect the DOM tree of the target website and determine what would work best. That’s the case unless the site provides an API specifically designed for scrapers.</p>
<p>Here is how this looks like in practice. This is a screenshot from the Airbnb website:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736588995918/fe2eb87f-e1cb-4474-894f-169fffb8a216.png" alt="Finding a selector from Airbnb website DOM tree" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Usually, you’ll need the information you’re scraping for some particular purpose, which means you’ll need to store it somewhere and then process it. This processing often involve some kind of visualization of the data. This is where our client application comes into play.</p>
<p>We will store the results of our scraping in the browser's local storage. Then, we will easily display those results in a grid layout using React and TailwindCSS. But before we get to all this, let’s go back to the code to understand how the scraping process is done.</p>
<h3 id="heading-set-up-playwright">Set Up Playwright</h3>
<p>For the scraping functionality, we will use another library that has gotten pretty famous over the last few years: <a target="_blank" href="https://playwright.dev/">Playwright</a>. It mainly serves as an e2e testing solution, but, as you’ll see now, we can use it for scraping the web as well.</p>
<p>We will put the scraping function in a separate file so that we have it all in order and keep the separation of concerns in place.</p>
<p>Create a new folder in the root directory and name it utils. Inside this folder, add a new file named scraper.js and include the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> MAX_RETRIES = <span class="hljs-number">3</span>;

<span class="hljs-keyword">const</span> validateListing = <span class="hljs-function">(<span class="hljs-params">listing</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="hljs-keyword">typeof</span> listing.title === <span class="hljs-string">"string"</span> &amp;&amp;
    <span class="hljs-keyword">typeof</span> listing.price === <span class="hljs-string">"string"</span> &amp;&amp;
    <span class="hljs-keyword">typeof</span> listing.link === <span class="hljs-string">"string"</span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> scrapeListings = <span class="hljs-keyword">async</span> ({ browser, retryCount }) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> page.goto(<span class="hljs-string">"https://www.airbnb.com/"</span>, { <span class="hljs-attr">waitUntil</span>: <span class="hljs-string">"load"</span> });

      <span class="hljs-keyword">await</span> page.waitForSelector(<span class="hljs-string">'[itemprop="itemListElement"]'</span>, {
        <span class="hljs-attr">timeout</span>: <span class="hljs-number">10000</span>,
      });

      <span class="hljs-keyword">const</span> listings = <span class="hljs-keyword">await</span> page.$$eval(
        <span class="hljs-string">'[itemprop="itemListElement"]'</span>,
        <span class="hljs-function">(<span class="hljs-params">elements</span>) =&gt;</span> {
          <span class="hljs-keyword">return</span> elements.slice(<span class="hljs-number">0</span>, <span class="hljs-number">10</span>).map(<span class="hljs-function">(<span class="hljs-params">element</span>) =&gt;</span> {
            <span class="hljs-keyword">const</span> title =
              element.querySelector(<span class="hljs-string">".t1jojoys"</span>)?.innerText || <span class="hljs-string">"N/A"</span>;
            <span class="hljs-keyword">const</span> price =
              element.querySelector(<span class="hljs-string">"._11jcbg2"</span>)?.innerText || <span class="hljs-string">"N/A"</span>;
            <span class="hljs-keyword">const</span> link = element.querySelector(<span class="hljs-string">"a"</span>)?.href || <span class="hljs-string">"N/A"</span>;
            <span class="hljs-keyword">return</span> { title, price, link };
          });
        }
      );

      <span class="hljs-keyword">const</span> validListings = listings.filter(validateListing);

      <span class="hljs-keyword">if</span> (validListings.length === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"No listings found"</span>);
      }

      <span class="hljs-keyword">return</span> validListings;
    } <span class="hljs-keyword">catch</span> (pageError) {
      <span class="hljs-keyword">if</span> (retryCount &lt; MAX_RETRIES) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Retrying... (<span class="hljs-subst">${retryCount + <span class="hljs-number">1</span>}</span>/<span class="hljs-subst">${MAX_RETRIES}</span>)`</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> scrapeListings(retryCount + <span class="hljs-number">1</span>);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
          <span class="hljs-string">`Failed to scrape data after <span class="hljs-subst">${MAX_RETRIES}</span> attempts: <span class="hljs-subst">${pageError.message}</span>`</span>
        );
      }
    } <span class="hljs-keyword">finally</span> {
      <span class="hljs-keyword">await</span> page.close();
    }
  } <span class="hljs-keyword">catch</span> (browserError) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Failed to launch browser: <span class="hljs-subst">${browserError.message}</span>`</span>);
  } <span class="hljs-keyword">finally</span> {
    <span class="hljs-keyword">if</span> (browser) {
      <span class="hljs-keyword">await</span> browser.close();
    }
  }
};
</code></pre>
<h3 id="heading-retry-mechanism">Retry Mechanism</h3>
<p>At the top of the file, there's a constant called <code>MAX_RETRIES</code> used to implement a <strong>retry mechanism</strong>. This tactic is often used by web scrapers to bypass or overcome protections that some websites have against scraping. We will see how to use it below.</p>
<p>It's important to mention the legal aspect here as well. Always respect the terms and conditions along with the privacy policy of the website you plan to scrape. Use these techniques only to handle technical challenges, not to break the law.</p>
<p>A small helper function follows that you can use to validate the received data. Nothing interesting here.</p>
<p>Next is the main scraping function. We’re passing the browser object, provided by Playwright, and the number of retry attempts as arguments to the function.</p>
<p>There are two try/catch blocks to handle possible failures: one for launching the browser (in headless mode, meaning you won't see anything) and one for the scraping process. In the latter, we’ll use Playwright's features to request the website, wait until the page is fully loaded, and then locate the selector we defined.</p>
<p>In the callback function we pass to <code>$$eval</code>, we access the elements returned by the scraping, allowing us to process them and get the data we want. In this case, I’m using three selectors to fetch the title, price, and link of the property. The first two are class names, and the last is the HTML tag <code>&lt;a&gt;</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736589673793/79cd8e05-eef7-4887-b3cb-6d2d95e9d5dc.png" alt="Selecting the price" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Then we return an object, { title, price, link }, with the fetched data, that is the values of the three properties. And in the end of the try part, we validate the results before returning them to the front end.</p>
<p>What follows in the catch part is the implementation of the retry mechanism we talked about a minute ago:</p>
<pre><code class="lang-javascript"> } <span class="hljs-keyword">catch</span> (pageError) {
      <span class="hljs-keyword">if</span> (retryCount &lt; MAX_RETRIES) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Retrying... (<span class="hljs-subst">${retryCount + <span class="hljs-number">1</span>}</span>/<span class="hljs-subst">${MAX_RETRIES}</span>)`</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> scrapeListings(retryCount + <span class="hljs-number">1</span>);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(
          <span class="hljs-string">`Failed to scrape data after <span class="hljs-subst">${MAX_RETRIES}</span> attempts: <span class="hljs-subst">${pageError.message}</span>`</span>
        );
      }
    }
</code></pre>
<p>If an error occurs during the reading process, we enter the catch phase and check if the retry count is below the maximum limit we set. If it is, we try again by running the function recursively. Otherwise, we throw an error indicating that the scraping failed and the maximum retry attempts have been reached.</p>
<p>That's all you need to set up basic web scraping of the Airbnb homepage.</p>
<p>You can see all this in the <a target="_blank" href="https://github.com/mihailgaberov/web-scraper">Github repo</a> of the project so don’t worry if you missed something here.</p>
<h2 id="heading-how-to-build-the-front-end">How to Build the Front End</h2>
<p>Now it's time to put the scraped data to use.</p>
<p>Let's display the last 10 properties in a grid layout, allowing you (or anyone) to open them by clicking on their links. We will also add a <code>Refresh</code> feature that lets you perform a new scrape to get the most up-to-date data.</p>
<p>This is how the structure of the front end part of the project looks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736591529574/5cf5c62f-283b-4cfc-afa4-3b2b92a3ddae.png" alt="Project structure" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>We have a simple app structure: one main container (App.jsx) that holds all the components and includes some logic for making requests to the API and storing the data in local storage.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useLocalStorage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@uidotdev/usehooks"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Footer"</span>;
<span class="hljs-keyword">import</span> Header <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Header"</span>;
<span class="hljs-keyword">import</span> RefreshButton <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/RefreshButton"</span>;
<span class="hljs-keyword">import</span> Grid <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Grid"</span>;
<span class="hljs-keyword">import</span> Loader <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/Loader"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [listings, setListings] = useLocalStorage(<span class="hljs-string">"properties"</span>, []);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> fetchListings = <span class="hljs-keyword">async</span> () =&gt; {
    setLoading(<span class="hljs-literal">true</span>);
    setError(<span class="hljs-string">""</span>);
    setListings([]);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">"http://localhost:5001/scrape"</span>);
      <span class="hljs-keyword">if</span> (response.data.length === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"No listings found"</span>);
      }
      setListings(response.data);
    } <span class="hljs-keyword">catch</span> (err) {
      setError(
        err.response?.data?.error ||
          <span class="hljs-string">"Failed to fetch listings. Please try again."</span>
      );
    } <span class="hljs-keyword">finally</span> {
      setLoading(<span class="hljs-literal">false</span>);
    }
  };

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (listings.length === <span class="hljs-number">0</span>) {
      fetchListings();
    }
  }, []);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center justify-center min-h-screen bg-gray-100"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">RefreshButton</span> <span class="hljs-attr">callback</span>=<span class="hljs-string">{fetchListings}</span> <span class="hljs-attr">loading</span>=<span class="hljs-string">{loading}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center justify-center flex-1 w-full px-4 relative"</span>&gt;</span>
        {error &amp;&amp; <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-red-500"</span>&gt;</span>{error}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}
        {loading ? <span class="hljs-tag">&lt;<span class="hljs-name">Loader</span> /&gt;</span> : <span class="hljs-tag">&lt;<span class="hljs-name">Grid</span> <span class="hljs-attr">listings</span>=<span class="hljs-string">{listings}</span> /&gt;</span>}
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>All components are placed in the <a target="_blank" href="https://github.com/mihailgaberov/web-scraper/tree/main/src/components">components</a> directory (this is what I call a surprise, ah:)). Most of the components are quite simple, and I included them to give the app a more complete appearance.</p>
<p>The <a target="_blank" href="https://github.com/mihailgaberov/web-scraper/blob/main/src/components/Header.jsx">Header</a> displays the top bar. The <a target="_blank" href="https://github.com/mihailgaberov/web-scraper/blob/main/src/components/RefreshButton.jsx">RefreshButton</a> is used to send a new request and get the latest data. In the <code>&lt;main&gt;</code> section, we either show an error message if fetching fails, or we display a <a target="_blank" href="https://github.com/mihailgaberov/web-scraper/blob/main/src/components/Loader.jsx">Loader</a> component and a <a target="_blank" href="https://github.com/mihailgaberov/web-scraper/blob/main/src/components/Grid.jsx">Grid</a> component.</p>
<p>The loading part is straightforward. The Grid component is the interesting one. We pass the scraping results to it using a prop called 'listings'. Inside, we use a simple map() function to go through them and display the properties. We use Tailwind to style the grid, ensuring the properties are neatly listed and look good on both desktop and mobile screens.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736593771753/0f56930c-2944-452c-a2cd-4c6b69c1d996.png" alt="The app looks good on smaller screens as well." class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>And in the end with we have the <a target="_blank" href="https://github.com/mihailgaberov/web-scraper/blob/main/src/components/Footer.jsx">Footer</a> component that renders a simple bar with text. Again, I’ve added it just for completeness.</p>
<h2 id="heading-how-to-deploy-to-rendercom">How to Deploy to render.com</h2>
<p>Maybe a little over a year ago, I needed a place to deploy full-stack applications, ideally for free, since they were just for educational purposes.</p>
<p>After some research, I found a platform called <a target="_blank" href="https://dashboard.render.com/">Render</a> and managed to deploy an app with both client and server parts, getting it to work online. I left it there until now. Since our scraper requires both parts to function properly, we will deploy it there and have it working online, as you can see <a target="_blank" href="https://scraper-fe.onrender.com/">here</a>.</p>
<p>To do this, you need to create an account with Render and use their dashboard application. The process is simple, but I'll include a few screenshots below for clarity.</p>
<p>This is the Overview page where you can see all your projects:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736594350231/eac21d82-f43d-44be-8027-6ac25c86f740.png" alt="Render platform overview page" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Here is the Project page where you can view and manage your projects. In our case, we can see both the server and the client app as separate services.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736594435708/a4329d74-f529-46a0-a193-d6a1ade145ad.png" alt="a4329d74-f529-46a0-a193-d6a1ade145ad" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You can click on each service to open its page, where you can view the deployments and the commits that triggered them. You can find even more details if you explore further.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736594566165/39e78a87-d8e7-4df3-8e1b-02eb0eee799c.png" alt="39e78a87-d8e7-4df3-8e1b-02eb0eee799c" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You should be able to manage the deployment process on your own, as everything is clearly explained. But if you need help, feel free to reach out.</p>
<p>I should mention that I am not affiliated with Render in any way and I am not receiving any benefits for mentioning them here. I just found it to be a useful tool and wanted to share it with you – so I’ve used it here.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>A web scraper app can be a powerful tool for gathering data, but there are several areas for improvement and important considerations to keep in mind here.</p>
<p>Firstly, you can enhance the app's performance and efficiency by optimizing the scraping process and ensuring that the data is stored and processed effectively. You can also implement more robust error handling and retry mechanisms to improve the reliability of the scraper.</p>
<p>Also, keep in mind that ethical scraping is crucial, and it's important to always respect the terms of service and privacy policies of the websites you are scraping. This includes not overloading the website with requests and ensuring that the data is used responsibly. Always seek permission from the site if required and consider using APIs provided by the website as a more ethical and reliable alternative.</p>
<p>Lastly, respecting the law is paramount. Ensure that your scraping activities comply with legal regulations and guidelines to avoid any potential legal issues. By focusing on these aspects, you can build a more effective, ethical, and legally compliant web scraper app.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Memory Card Game Using React ]]>
                </title>
                <description>
                    <![CDATA[ Recently, while watching my kid 🧒🏻 playing free memory games on her tablet, I noticed her struggling with an overwhelming number of ads and annoying pop-up banners. This inspired me to build a similar game for her. Since she's currently into anime,... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-memory-card-game-using-react/</link>
                <guid isPermaLink="false">674755a7cc41aab7422d5afb</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Testing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kids ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Anime ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Wed, 27 Nov 2024 17:23:51 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732517515517/1ddfb635-6188-492a-9216-4b35ffb92096.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recently, while watching my kid 🧒🏻 playing free memory games on her tablet, I noticed her struggling with an overwhelming number of ads and annoying pop-up banners.</p>
<p>This inspired me to build a similar game for her. Since she's currently into anime, I decided to create the game using cute anime-style images.</p>
<p>In this article, I'll walk you through the process of building the game for yourself or your kids 🎮.</p>
<p>We'll begin by exploring the game features, then cover the tech stack and project structure—both of which are straightforward. Finally, we'll discuss optimizations and ensuring smooth gameplay on mobile devices 📱.</p>
<p>If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/memory-card">here</a> 💁 is the GitHub repository 🙌. And <a target="_blank" href="https://memory-card-blush-pi.vercel.app/">here</a> you can see the live demo.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-description">Project Description</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-build-the-game">Let’s Build the Game</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-testing-the-app">Testing the App</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-api-fallback-mechanism">API Fallback Mechanism</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-optimizations">Optimizations</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-future-improvements">Future Improvements</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-project-description">Project Description</h2>
<p>In this tutorial, we’ll build a challenging memory card game with React that tests your recall abilities. Your goal is to click unique anime images without clicking the same one twice. Each unique click earns you points, but be careful—clicking an image twice resets your progress.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732517597049/8677428e-ebd6-4f0b-a1f6-2a2d9f5f2dd2.png" alt="Memory Card game screenshot" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-game-features">Game features:</h3>
<ul>
<li><p>🎯 Dynamic gameplay that challenges your memory</p>
</li>
<li><p>🔄 Cards shuffle after each click to increase difficulty</p>
</li>
<li><p>🏆 Score tracking with best score persistence</p>
</li>
<li><p>😺 Adorable anime images from The Nekosia API</p>
</li>
<li><p>✨ Smooth loading transitions and animations</p>
</li>
<li><p>📱 Responsive design for all devices</p>
</li>
<li><p>🎨 Clean, modern UI</p>
</li>
</ul>
<p>The game will help you test your memory skills while enjoying cute anime pictures. Can you achieve the perfect score?</p>
<h3 id="heading-how-to-play">How to Play</h3>
<ol>
<li><p>Click on any card to start</p>
</li>
<li><p>Remember which cards you've clicked</p>
</li>
<li><p>Try to click all cards exactly once</p>
</li>
<li><p>Watch your score grow with each unique selection</p>
</li>
<li><p>Then keep playing to try to beat your best score</p>
</li>
</ol>
<h3 id="heading-the-tech-stack">The Tech Stack</h3>
<p>Here’s a list of the main technologies we’ll be using:</p>
<ul>
<li><p><strong>NPM</strong> – A package manager for JavaScript that helps manage dependencies and scripts for the project.</p>
</li>
<li><p><strong>Vite</strong> – A build tool that provides a fast development environment, particularly optimized for modern web projects.</p>
</li>
<li><p><strong>React</strong> – A popular JavaScript library for building user interfaces, enabling efficient rendering and state management.</p>
</li>
<li><p><strong>CSS Modules</strong> – A styling solution that scopes CSS to individual components, preventing style conflicts and ensuring maintainability.</p>
</li>
</ul>
<h2 id="heading-lets-build-the-game">Let’s Build the Game</h2>
<p>From this point onward, I will guide you through the process I followed when building this game.</p>
<h3 id="heading-project-structure-and-architecture">Project Structure and Architecture</h3>
<p>When building this memory card game, I carefully organized the codebase to ensure maintainability, scalability, and clear separation of concerns. Let's explore the structure and the reasoning behind each decision:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732517663524/648124f0-aa8c-4c50-9292-4bdbd1c9c4db.png" alt="Project Structure" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-component-based-architecture">Component-Based Architecture</h4>
<p>I chose a component-based architecture for several reasons:</p>
<ul>
<li><p><strong>Modularity</strong>: Each component is self-contained with its own logic and styles</p>
</li>
<li><p><strong>Reusability</strong>: Components like <code>Card</code> and <code>Loader</code> can be reused across the application</p>
</li>
<li><p><strong>Maintainability</strong>: Easier to debug and modify individual components</p>
</li>
<li><p><strong>Testing</strong>: Components can be tested in isolation</p>
</li>
</ul>
<h4 id="heading-component-organization">Component Organization</h4>
<ol>
<li>Card Component</li>
</ol>
<ul>
<li><p>Separated into its own directory because it's a core game element</p>
</li>
<li><p>Contains both JSX and SCSS modules for encapsulation</p>
</li>
<li><p>Handles individual card rendering, loading states, and click events</p>
</li>
</ul>
<ol start="2">
<li>CardsGrid Component</li>
</ol>
<ul>
<li><p>Manages the game board layout</p>
</li>
<li><p>Handles card shuffling and distribution</p>
</li>
<li><p>Controls the responsive grid layout for different screen sizes</p>
</li>
</ul>
<ol start="3">
<li>Loader Component</li>
</ol>
<ul>
<li><p>Reusable loading indicator</p>
</li>
<li><p>Improves user experience during image loading</p>
</li>
<li><p>Can be used by any component that needs loading states</p>
</li>
</ul>
<ol start="4">
<li>Header/Footer/Subtitle Components</li>
</ol>
<ul>
<li><p>Structural components for app layout</p>
</li>
<li><p>Header displays game title and scores</p>
</li>
<li><p>Footer shows copyright and version info</p>
</li>
<li><p>Subtitle provides game instructions</p>
</li>
</ul>
<h4 id="heading-css-modules-approach">CSS Modules Approach</h4>
<p>I used CSS Modules (<code>.module.scss</code> files) for several benefits:</p>
<ul>
<li><p><strong>Scoped Styling</strong>: Prevents style leaks between components</p>
</li>
<li><p><strong>Name Collisions</strong>: Automatically generates unique class names</p>
</li>
<li><p><strong>Maintainability</strong>: Styles are co-located with their components</p>
</li>
<li><p><strong>SCSS Features</strong>: Leverages SCSS features while keeping styles modular</p>
</li>
</ul>
<h4 id="heading-custom-hooks">Custom Hooks</h4>
<p>The <code>hooks</code> directory contains custom hooks like useFetch:</p>
<ul>
<li><p><strong>Separation of Concerns</strong>: Isolates data fetching logic</p>
</li>
<li><p><strong>Reusability</strong>: Can be used by any component needing image data</p>
</li>
<li><p><strong>State Management</strong>: Handles loading, error, and data states</p>
</li>
<li><p><strong>Performance</strong>: Implements optimizations like image size control</p>
</li>
</ul>
<h4 id="heading-root-level-files">Root Level Files</h4>
<h4 id="heading-appjsx">App.jsx:</h4>
<ul>
<li><p>Acts as the application's entry point</p>
</li>
<li><p>Manages global state and routing (if needed)</p>
</li>
<li><p>Coordinates component composition</p>
</li>
<li><p>Handles top-level layouts</p>
</li>
</ul>
<h4 id="heading-performance-considerations">Performance Considerations</h4>
<p>The structure supports performance optimizations:</p>
<ul>
<li><p><strong>Code Splitting</strong>: Components can be lazy-loaded if needed</p>
</li>
<li><p><strong>Memoization</strong>: Components can be memoized effectively</p>
</li>
<li><p><strong>Style Loading</strong>: CSS Modules enable efficient style loading</p>
</li>
<li><p><strong>Asset Management</strong>: Images and resources are properly organized</p>
</li>
</ul>
<h4 id="heading-scalability">Scalability</h4>
<p>This structure allows for easy scaling:</p>
<ul>
<li><p>New features can be added as new components</p>
</li>
<li><p>Additional hooks can be created for new functionality</p>
</li>
<li><p>Styles remain maintainable as the app grows</p>
</li>
<li><p>Testing can be implemented at any level</p>
</li>
</ul>
<h4 id="heading-development-experience">Development Experience</h4>
<p>The structure enhances developer experience:</p>
<ul>
<li><p>Clear file organization</p>
</li>
<li><p>Intuitive component locations</p>
</li>
<li><p>Easy to find and modify specific features</p>
</li>
<li><p>Supports efficient collaboration</p>
</li>
</ul>
<p>This architecture proved particularly valuable when optimizing the game for tablet use, as it allowed me to:</p>
<ol>
<li><p>Easily identify and optimize performance bottlenecks</p>
</li>
<li><p>Add tablet-specific styles without affecting other devices</p>
</li>
<li><p>Implement loading states for better mobile experience</p>
</li>
<li><p>Maintain clean separation between game logic and UI components</p>
</li>
</ol>
<p>Alright, now let’s get coding.</p>
<h2 id="heading-step-by-step-build-guide">Step-by-Step Build Guide</h2>
<h3 id="heading-1-project-setup">1. Project Setup</h3>
<p><strong>Set Up the Development Environment</strong></p>
<p>In order to start with a clean React project, open your terminal app and run the following commands (you may name your project folder as you like – in my case the name is ‘memory-card’):</p>
<pre><code class="lang-bash">npm create vite@latest memory-card -- --template react
<span class="hljs-built_in">cd</span> memory-card
npm install
</code></pre>
<p><strong>Install the Required Dependencies</strong></p>
<p>The only dependencies we will use in this project are the hook package from UI.dev (by the way, <a target="_blank" href="https://ui.dev/why-react-renders">here</a> you can find a well-explained article on how rendering in React works).</p>
<p>The other dependency is the famous CSS preprocessor, <a target="_blank" href="https://sass-lang.com/">SASS</a>, that we’ll need to be able to write our CSS modules in SASS instead of regular CSS.</p>
<pre><code class="lang-javascript">npm install @uidotdev/usehooks sass
</code></pre>
<p><strong>Configure Vite and Project Setting</strong></p>
<p>When setting up our project, we need to make some specific configuration adjustments to handle SASS warnings and improve our development experience. Here's how you can configure Vitest:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// vitest.config.js</span>
<span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest/config'</span>;
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">'@vitejs/plugin-react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">plugins</span>: [react()],
  <span class="hljs-attr">test</span>: {
    <span class="hljs-attr">environment</span>: <span class="hljs-string">'jsdom'</span>,
    <span class="hljs-attr">globals</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">setupFiles</span>: [<span class="hljs-string">'./src/setupTests.js'</span>],
    <span class="hljs-attr">css</span>: {
      <span class="hljs-attr">modules</span>: {
        <span class="hljs-attr">classNameStrategy</span>: <span class="hljs-string">'non-scoped'</span>
      }
    },
    <span class="hljs-attr">preprocessors</span>: {
      <span class="hljs-string">'**/*.scss'</span>: <span class="hljs-string">'sass'</span>
    },
    <span class="hljs-attr">coverage</span>: {
      <span class="hljs-attr">provider</span>: <span class="hljs-string">'v8'</span>,
      <span class="hljs-attr">reporter</span>: [<span class="hljs-string">'text'</span>, <span class="hljs-string">'json'</span>, <span class="hljs-string">'html'</span>],
      <span class="hljs-attr">exclude</span>: [
        <span class="hljs-string">'node_modules/'</span>,
        <span class="hljs-string">'src/setupTests.js'</span>,
        <span class="hljs-string">'src/main.jsx'</span>,
        <span class="hljs-string">'src/vite-env.d.ts'</span>,
      ],
    },
  },
  <span class="hljs-attr">css</span>: {
    <span class="hljs-attr">preprocessorOptions</span>: {
      <span class="hljs-attr">scss</span>: {
        <span class="hljs-attr">quietDeps</span>: <span class="hljs-literal">true</span>,  <span class="hljs-comment">// Silences SASS dependency warnings</span>
        <span class="hljs-attr">charset</span>: <span class="hljs-literal">false</span>    <span class="hljs-comment">// Prevents charset warning in recent SASS versions</span>
      }
    }
  }
});
</code></pre>
<p>Keep in mind that most of these configurations are auto-generated for you when you create the project with Vite. Here’s what’s going on:</p>
<ol>
<li><p><strong>SASS Configuration</strong>:</p>
<ul>
<li><p><code>quietDeps: true</code>: This silences the warnings about deprecated dependencies in SASS modules. Particularly useful when working with third-party SASS/SCSS files.</p>
</li>
<li><p><code>charset: false</code>: Prevents the "@charset" warning that appears in newer versions of SASS when using special characters in your stylesheets.</p>
</li>
</ul>
</li>
<li><p><strong>Test Configuration</strong>:</p>
<ul>
<li><p><code>globals: true</code>: Makes test functions globally available in test files</p>
</li>
<li><p><code>environment: 'jsdom'</code>: Provides a DOM environment for testing</p>
</li>
<li><p><code>setupFiles</code>: Points to our test setup file</p>
</li>
</ul>
</li>
</ol>
<p>These configurations help create a cleaner development experience by removing unnecessary warning messages in the console, setting up proper test environment configurations, and ensuring SASS/SCSS processing works smoothly.</p>
<p>You might see warnings in your console without these configurations when:</p>
<ul>
<li><p>Using SASS/SCSS features or importing SASS files</p>
</li>
<li><p>Running tests that require DOM manipulation</p>
</li>
<li><p>Using special characters in your stylesheets</p>
</li>
</ul>
<h3 id="heading-2-building-the-components"><strong>2. Building the Components</strong></h3>
<p><strong>Create the Card Component</strong></p>
<p>First, let's create our basic card component that will display individual images:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/components/Card/Card.jsx</span>
<span class="hljs-keyword">import</span> React, { useState, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Loader <span class="hljs-keyword">from</span> <span class="hljs-string">"../Loader"</span>;
<span class="hljs-keyword">import</span> styles <span class="hljs-keyword">from</span> <span class="hljs-string">"./Card.module.scss"</span>;

<span class="hljs-keyword">const</span> Card = React.memo(<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Card</span>(<span class="hljs-params">{ imgUrl, imageId, categoryName, processTurn }</span>) </span>{
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">true</span>);

  <span class="hljs-keyword">const</span> handleImageLoad = useCallback(<span class="hljs-function">() =&gt;</span> {
    setIsLoading(<span class="hljs-literal">false</span>);
  }, []);

  <span class="hljs-keyword">const</span> handleClick = useCallback(<span class="hljs-function">() =&gt;</span> {
    processTurn(imageId);
  }, [processTurn, imageId]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{styles.container}</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{handleClick}</span>&gt;</span>
      {isLoading &amp;&amp; (
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{styles.loaderContainer}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Loader</span> <span class="hljs-attr">message</span>=<span class="hljs-string">"Loading..."</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      )}
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span>
        <span class="hljs-attr">src</span>=<span class="hljs-string">{imgUrl}</span>
        <span class="hljs-attr">alt</span>=<span class="hljs-string">{categoryName}</span>
        <span class="hljs-attr">onLoad</span>=<span class="hljs-string">{handleImageLoad}</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">styles.image</span>} ${<span class="hljs-attr">isLoading</span> ? <span class="hljs-attr">styles.hidden</span> <span class="hljs-attr">:</span> ''}`}
      /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Card;
</code></pre>
<p>The Card component is a fundamental building block of our game. It's responsible for displaying individual images and handling player interactions. Let's break down its implementation:</p>
<h4 id="heading-props-breakdown"><strong>Props breakdown:</strong></h4>
<ol>
<li><p><code>image</code>: (string)</p>
<ul>
<li><p>The URL of the image to be displayed that’s received from our API service.</p>
</li>
<li><p>It’s used directly in the img tag's src attribute.</p>
</li>
</ul>
</li>
<li><p><code>id</code>: (string)</p>
<ul>
<li><p>Unique identifier for each card that’s critical for tracking which cards have been clicked.</p>
</li>
<li><p>It’s passed to the <code>processTurn</code> callback when a card is clicked.</p>
</li>
</ul>
</li>
<li><p><code>category</code>: (string)</p>
<ul>
<li><p>Describes the type of image (for example, "anime", "neko"), and it’s used in the alt attribute for better accessibility.</p>
</li>
<li><p>It helps with SEO and screen readers.</p>
</li>
</ul>
</li>
<li><p><code>processTurn</code>: (function)</p>
<ul>
<li><p>Callback function passed from the parent component that handles the game logic when a card is clicked.</p>
</li>
<li><p>It also manages score updates and game state changes and determines if a card has been clicked before.</p>
</li>
</ul>
</li>
<li><p><code>isLoading</code>: (boolean)</p>
<ul>
<li><p>Controls whether to show a loading state. When true, it displays a Loader component instead of the image.</p>
</li>
<li><p>It improves the user experience during image loading.</p>
</li>
</ul>
</li>
</ol>
<h4 id="heading-component-styling"><strong>Component styling:</strong></h4>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/components/Card/Card.module.scss</span>
.container {
  <span class="hljs-attr">display</span>: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: rgba(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.8</span>);
  border: <span class="hljs-number">1</span>px solid rgba(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.8</span>);
  padding: <span class="hljs-number">20</span>px;
  font-size: <span class="hljs-number">30</span>px;
  text-align: center;
  min-height: <span class="hljs-number">200</span>px;
  position: relative;
  cursor: pointer;
  transition: transform <span class="hljs-number">0.2</span>s ease;

  &amp;:hover {
    <span class="hljs-attr">transform</span>: scale(<span class="hljs-number">1.02</span>);
  }

  .image {
    <span class="hljs-attr">width</span>: <span class="hljs-number">10</span>rem;
    height: auto;
    opacity: <span class="hljs-number">1</span>;
    transition: opacity <span class="hljs-number">0.3</span>s ease;

    &amp;.hidden {
      <span class="hljs-attr">opacity</span>: <span class="hljs-number">0</span>;
    }
  }

  .loaderContainer {
    <span class="hljs-attr">position</span>: absolute;
    top: <span class="hljs-number">50</span>%;
    left: <span class="hljs-number">50</span>%;
    transform: translate(<span class="hljs-number">-50</span>%, <span class="hljs-number">-50</span>%);
  }
}
</code></pre>
<h4 id="heading-usage-in-the-component"><strong>Usage in the component:</strong></h4>
<pre><code class="lang-javascript">&lt;Card
    key={getKey()}
    imgUrl={item?.image?.original?.url || <span class="hljs-string">""</span>}
    imageId={item?.id}
    categoryName={item?.category}
    processTurn={<span class="hljs-function">(<span class="hljs-params">imageId</span>) =&gt;</span> processTurn(imageId)} 
/&gt;
</code></pre>
<h4 id="heading-key-features"><strong>Key features:</strong></h4>
<ol>
<li><p><strong>Performance Optimization</strong>:</p>
<ul>
<li><p>Uses <code>React.memo</code> to prevent unnecessary re-renders</p>
</li>
<li><p>Implements <code>useCallback</code> for event handlers</p>
</li>
<li><p>Manages loading state internally for better UX</p>
</li>
</ul>
</li>
<li><p><strong>Loading State Management</strong>:</p>
<ul>
<li><p>Internal <code>isLoading</code> state tracks image loading</p>
</li>
<li><p>Shows a Loader component with a message while loading</p>
</li>
<li><p>Hides the image until it's fully loaded using CSS classes</p>
</li>
</ul>
</li>
<li><p><strong>Event Handling</strong>:</p>
<ul>
<li><p><code>handleImageLoad</code>: Manages the loading state transition</p>
</li>
<li><p><code>handleClick</code>: Processes player moves via the <code>processTurn</code> callback</p>
</li>
</ul>
</li>
</ol>
<p><strong>Build the CardsGrid Component</strong></p>
<p>This is our main game component that manages the game state, scoring logic, and card interactions. Let's break down its implementation:</p>
<pre><code class="lang-javascript">
<span class="hljs-comment">// src/components/CardsGrid/CardsGrid.jsx</span>
<span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { useLocalStorage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@uidotdev/usehooks"</span>;
<span class="hljs-keyword">import</span> Card <span class="hljs-keyword">from</span> <span class="hljs-string">"../Card"</span>;
<span class="hljs-keyword">import</span> Loader <span class="hljs-keyword">from</span> <span class="hljs-string">"../Loader"</span>;
<span class="hljs-keyword">import</span> styles <span class="hljs-keyword">from</span> <span class="hljs-string">"./CardsGrid.module.scss"</span>;
<span class="hljs-keyword">import</span> useFetch <span class="hljs-keyword">from</span> <span class="hljs-string">"../../hooks/useFetch"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CardsGrid</span>(<span class="hljs-params">data</span>) </span>{
  <span class="hljs-comment">// State Management</span>
  <span class="hljs-keyword">const</span> [images, setImages] = useState(data?.data?.images || []);
  <span class="hljs-keyword">const</span> [clickedImages, setClickedImages] = useLocalStorage(<span class="hljs-string">"clickedImages"</span>, []);
  <span class="hljs-keyword">const</span> [score, setScore] = useLocalStorage(<span class="hljs-string">"score"</span>, <span class="hljs-number">0</span>);
  <span class="hljs-keyword">const</span> [bestScore, setBestScore] = useLocalStorage(<span class="hljs-string">"bestScore"</span>, <span class="hljs-number">0</span>);
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(!data?.data?.images?.length);

  <span class="hljs-comment">// Custom hook for fetching images</span>
  <span class="hljs-keyword">const</span> { <span class="hljs-attr">data</span>: fetchedData, fetchData, error } = useFetch();

  <span class="hljs-comment">// Update images when new data is fetched</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (fetchedData?.images) {
      setImages(fetchedData.images);
      setIsLoading(<span class="hljs-literal">false</span>);
      <span class="hljs-comment">// Reset clicked images when new batch is loaded</span>
      setClickedImages([]);
    }
  }, [fetchedData]);

  <span class="hljs-comment">// Helper function to update best score</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateBestScore</span>(<span class="hljs-params">currentScore</span>) </span>{
    <span class="hljs-keyword">if</span> (currentScore &gt; bestScore) {
      setBestScore(currentScore);
    }
  }

  <span class="hljs-comment">// Core game logic</span>
  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">processTurn</span>(<span class="hljs-params">imageId</span>) </span>{
    <span class="hljs-keyword">const</span> newClickedImages = [...clickedImages, imageId];
    setClickedImages(newClickedImages);

    <span class="hljs-comment">// If clicking the same image twice, reset everything</span>
    <span class="hljs-keyword">if</span> (clickedImages.includes(imageId)) {
      <span class="hljs-comment">// Update the best score if necessary</span>
      updateBestScore(score);

      setClickedImages([]);
      setScore(<span class="hljs-number">0</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// Handle successful card selection</span>
      <span class="hljs-keyword">const</span> newScore = score + <span class="hljs-number">1</span>;
      setScore(newScore);

      <span class="hljs-comment">// Check for perfect score (all cards clicked once)</span>
       <span class="hljs-keyword">if</span> (newClickedImages.length === images.length) {
        updateBestScore(newScore);
        fetchData();
        setClickedImages([]);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// Shuffle the images</span>
        <span class="hljs-keyword">const</span> shuffled = [...images].sort(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Math</span>.random() - <span class="hljs-number">0.5</span>);
        setImages(shuffled);
      }
    }
  }

 <span class="hljs-keyword">if</span> (error) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Failed to fetch data<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;
  }

  <span class="hljs-keyword">if</span> (isLoading) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Loader</span> <span class="hljs-attr">message</span>=<span class="hljs-string">"Loading new images..."</span> /&gt;</span></span>;
  }

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{styles.container}</span>&gt;</span>
      {images.map((item) =&gt; (
        <span class="hljs-tag">&lt;<span class="hljs-name">Card</span>
          <span class="hljs-attr">key</span>=<span class="hljs-string">{getKey()}</span>
          <span class="hljs-attr">imgUrl</span>=<span class="hljs-string">{item?.image?.original?.url</span> || ""}
          <span class="hljs-attr">imageId</span>=<span class="hljs-string">{item?.id}</span>
          <span class="hljs-attr">categoryName</span>=<span class="hljs-string">{item?.category}</span>
          <span class="hljs-attr">processTurn</span>=<span class="hljs-string">{(imageId)</span> =&gt;</span> processTurn(imageId)}
        /&gt;
      ))}
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> React.memo(CardsGrid);
</code></pre>
<h4 id="heading-component-styling-1"><strong>Component styling:</strong></h4>
<pre><code class="lang-scss"><span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">display</span>: grid;
  gap: <span class="hljs-number">1rem</span> <span class="hljs-number">1rem</span>;
  grid-template-<span class="hljs-attribute">columns</span>: auto; <span class="hljs-comment">/* Default: one column for mobile-first */</span>
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#2196f3</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.7rem</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-keyword">@media</span> (min-width: <span class="hljs-number">481px</span>) {
  <span class="hljs-selector-class">.container</span> {
    grid-template-<span class="hljs-attribute">columns</span>: auto auto; <span class="hljs-comment">/* Two columns for tablets and up */</span>
  }
}

<span class="hljs-keyword">@media</span> (min-width: <span class="hljs-number">769px</span>) {
  <span class="hljs-selector-class">.container</span> {
    grid-template-<span class="hljs-attribute">columns</span>: auto auto auto; <span class="hljs-comment">/* Three columns for desktops and larger */</span>
  }
}
</code></pre>
<h4 id="heading-key-features-breakdown"><strong>Key Features Breakdown:</strong></h4>
<ol>
<li><p><strong>State Management</strong>:</p>
<ul>
<li><p>Uses <code>useState</code> for component-level state</p>
</li>
<li><p>Implements <code>useLocalStorage</code> for persistent game data:</p>
<ul>
<li><p><code>clickedImages</code>: Tracks which cards have been clicked</p>
</li>
<li><p><code>score</code>: Current game score</p>
</li>
<li><p><code>bestScore</code>: Highest score achieved</p>
</li>
</ul>
</li>
<li><p>Manages loading state for image fetching</p>
</li>
<li><p>Shuffle the cards</p>
</li>
</ul>
</li>
<li><p><strong>Game Logic</strong>:</p>
<ul>
<li><p><code>processTurn</code>: Handles player moves</p>
<ul>
<li><p>Tracks duplicate clicks</p>
</li>
<li><p>Updates scores</p>
</li>
<li><p>Manages perfect score scenarios</p>
</li>
</ul>
</li>
<li><p><code>updateBestScore</code>: Updates high score when necessary</p>
</li>
<li><p>Automatically fetches new images when a round is completed</p>
</li>
</ul>
</li>
<li><p><strong>Data Fetching</strong>:</p>
<ul>
<li><p>Uses custom <code>useFetch</code> hook for image data</p>
</li>
<li><p>Handles loading and error states</p>
</li>
<li><p>Updates images when new data is fetched</p>
</li>
</ul>
</li>
<li><p><strong>Performance Optimization</strong>:</p>
<ul>
<li><p>Component wrapped in <code>React.memo</code></p>
</li>
<li><p>Efficient state updates</p>
</li>
<li><p>Responsive grid layout</p>
</li>
</ul>
</li>
<li><p><strong>Persistence</strong>:</p>
<ul>
<li><p>Game state persists across page reloads</p>
</li>
<li><p>Best score tracking</p>
</li>
<li><p>Current game progress saving</p>
</li>
</ul>
</li>
</ol>
<h4 id="heading-usage-example"><strong>Usage Example:</strong></h4>
<pre><code class="lang-javascript">...
...

function App() {
  <span class="hljs-keyword">const</span> { data, loading, error } = useFetch();

  <span class="hljs-keyword">if</span> (loading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Loader</span> /&gt;</span></span>;
  <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Error: {error}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{styles.container}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Header</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Subtitle</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">CardsGrid</span> <span class="hljs-attr">data</span>=<span class="hljs-string">{data}</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>The CardsGrid component serves as the heart of our memory card game, managing:</p>
<ul>
<li><p>Game state and logic</p>
</li>
<li><p>Score tracking</p>
</li>
<li><p>Card interactions</p>
</li>
<li><p>Image loading and display</p>
</li>
<li><p>Responsive layout</p>
</li>
<li><p>Data persistence</p>
</li>
</ul>
<p>This implementation provides a smooth gaming experience while maintaining code readability and maintainability through clear separation of concerns and proper state management.</p>
<h3 id="heading-3-implementing-the-api-layer"><strong>3.</strong> Implementing the API Layer</h3>
<p>Our game uses a robust API layer with multiple fallback options to ensure reliable image delivery. Let's implement each service and the fallback mechanism.</p>
<p><strong>Set Up the Primary API Service:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/services/api/nekosiaApi.js</span>
<span class="hljs-keyword">const</span> NEKOSIA_API_URL = <span class="hljs-string">"https://api.nekosia.cat/api/v1/images/catgirl"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchNekosiaImages</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(
    <span class="hljs-string">`<span class="hljs-subst">${NEKOSIA_API_URL}</span>?count=21&amp;additionalTags=white-hair,uniform&amp;blacklistedTags=short-hair,sad,maid&amp;width=300`</span>
  );

  <span class="hljs-keyword">if</span> (!response.ok) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Nekosia API error: <span class="hljs-subst">${response.status}</span>`</span>);
  }

  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json();

  <span class="hljs-keyword">if</span> (!result.images || !<span class="hljs-built_in">Array</span>.isArray(result.images)) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Invalid response format from Nekosia API'</span>);
  }

  <span class="hljs-keyword">const</span> validImages = result.images.filter(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> item?.image?.original?.url);

  <span class="hljs-keyword">if</span> (validImages.length === <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'No valid images received from Nekosia API'</span>);
  }

  <span class="hljs-keyword">return</span> { ...result, <span class="hljs-attr">images</span>: validImages };
}
</code></pre>
<p><strong>Create the First Fallback API Service:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/services/api/nekosBestApi.js</span>
<span class="hljs-keyword">const</span> NEKOS_BEST_API_URL = <span class="hljs-string">"https://nekos.best/api/v2/neko?amount=21"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchNekosBestImages</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(NEKOS_BEST_API_URL, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
    <span class="hljs-attr">mode</span>: <span class="hljs-string">"no-cors"</span>
  });

  <span class="hljs-keyword">if</span> (!response.ok) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Nekos Best API error: <span class="hljs-subst">${response.status}</span>`</span>);
  }

  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json();

  <span class="hljs-comment">// Transform the response to match our expected format</span>
  <span class="hljs-keyword">const</span> transformedImages = result.results.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> ({
    <span class="hljs-attr">id</span>: item.url.split(<span class="hljs-string">'/'</span>).pop().split(<span class="hljs-string">'.'</span>)[<span class="hljs-number">0</span>], <span class="hljs-comment">// Extract UUID from URL</span>
    <span class="hljs-attr">image</span>: {
      <span class="hljs-attr">original</span>: {
        <span class="hljs-attr">url</span>: item.url
      }
    },
    <span class="hljs-attr">artist</span>: {
      <span class="hljs-attr">name</span>: item.artist_name,
      <span class="hljs-attr">href</span>: item.artist_href
    },
    <span class="hljs-attr">source</span>: item.source_url
  }));

  <span class="hljs-keyword">return</span> { <span class="hljs-attr">images</span>: transformedImages };
}
</code></pre>
<p><strong>Create the Second Fallback API Service:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/services/api/nekosApi.js</span>
<span class="hljs-keyword">const</span> NEKOS_API_URL = <span class="hljs-string">"https://api.nekosapi.com/v3/images/random?limit=21&amp;rating=safe"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchNekosImages</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(NEKOS_API_URL, {
    <span class="hljs-attr">method</span>: <span class="hljs-string">"GET"</span>,
  });

  <span class="hljs-keyword">if</span> (!response.ok) {
    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">`Nekos API error: <span class="hljs-subst">${response.status}</span>`</span>);
  }

  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> response.json();

  <span class="hljs-comment">// Transform the response to match our expected format</span>
  <span class="hljs-keyword">const</span> transformedImages = result.items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> ({
    <span class="hljs-attr">id</span>: item.id,
    <span class="hljs-attr">image</span>: {
      <span class="hljs-attr">original</span>: {
        <span class="hljs-attr">url</span>: item.image_url
      }
    }
  }));

  <span class="hljs-keyword">return</span> { <span class="hljs-attr">images</span>: transformedImages };
}
</code></pre>
<p><strong>Build the API Fallback Mechanism:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/services/api/imageService.js</span>
<span class="hljs-keyword">import</span> { fetchNekosiaImages } <span class="hljs-keyword">from</span> <span class="hljs-string">"./nekosiaApi"</span>;
<span class="hljs-keyword">import</span> { fetchNekosImages } <span class="hljs-keyword">from</span> <span class="hljs-string">"./nekosApi"</span>;
<span class="hljs-keyword">import</span> { fetchNekosBestImages } <span class="hljs-keyword">from</span> <span class="hljs-string">"./nekosBestApi"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchImages</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-comment">// Try primary API first</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> fetchNekosiaImages();
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Primary API failed, trying fallback:"</span>, error);

    <span class="hljs-comment">// Try first fallback API</span>
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> fetchNekosBestImages();
    } <span class="hljs-keyword">catch</span> (fallbackError) {
      <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"First fallback API failed, trying second fallback:"</span>, fallbackError);

      <span class="hljs-comment">// Try second fallback API</span>
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> fetchNekosImages();
      } <span class="hljs-keyword">catch</span> (secondFallbackError) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"All image APIs failed:"</span>, secondFallbackError);
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"All image APIs failed"</span>);
      }
    }
  }
}
</code></pre>
<p><strong>Use the Image Service:</strong></p>
<pre><code class="lang-javascript"><span class="hljs-comment">// src/hooks/useFetch.js</span>
<span class="hljs-keyword">import</span> { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { fetchImages } <span class="hljs-keyword">from</span> <span class="hljs-string">"../services/api/imageService"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useFetch</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [data, setData] = useState([]);
  <span class="hljs-keyword">const</span> [loading, setLoading] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);

  <span class="hljs-keyword">const</span> fetchData = <span class="hljs-keyword">async</span> () =&gt; {
    setLoading(<span class="hljs-literal">true</span>);
    setError(<span class="hljs-literal">null</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> fetchImages();
      setData(result);
    } <span class="hljs-keyword">catch</span> (err) {
      setError(err.message || <span class="hljs-string">'An error occurred'</span>);
    } <span class="hljs-keyword">finally</span> {
      setLoading(<span class="hljs-literal">false</span>);
    }
  };

  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchData();
  }, []);

  <span class="hljs-keyword">return</span> {
    data,
    loading,
    error,
    fetchData,
  };
}
</code></pre>
<h4 id="heading-key-features-of-our-api-implementation"><strong>Key Features of Our API Implementation:</strong></h4>
<ol>
<li><p><strong>Multiple API Sources</strong>:</p>
<ul>
<li><p>Primary API (Nekosia): Provides high-quality anime images</p>
</li>
<li><p>First Fallback (Nekos Best): Includes artist information</p>
</li>
<li><p>Second Fallback (Nekos): Simple and reliable backup</p>
</li>
</ul>
</li>
<li><p><strong>Consistent Data Format</strong>:</p>
<ul>
<li>All APIs transform their responses to match our expected format:</li>
</ul>
</li>
</ol>
<pre><code class="lang-json">    {
      images: [
        {
          id: string,
          image: {
            original: {
              url: string
            }
          }
        }
      ]
    }
</code></pre>
<ol start="3">
<li><p><strong>Robust Error Handling</strong>:</p>
<ul>
<li><p>Validates API responses</p>
</li>
<li><p>Checks for valid image URLs</p>
</li>
<li><p>Provides detailed error messages</p>
</li>
<li><p>Graceful fallback mechanism</p>
</li>
</ul>
</li>
<li><p><strong>Safety Features</strong>:</p>
<ul>
<li><p>Safe content filtering (<code>rating=safe</code>)</p>
</li>
<li><p>Image count limitation (21 images)</p>
</li>
<li><p>URL validation</p>
</li>
<li><p>Response format validation</p>
</li>
</ul>
</li>
<li><p><strong>Performance Considerations</strong>:</p>
<ul>
<li><p>Optimized image sizes</p>
</li>
<li><p>Filtered content tags</p>
</li>
<li><p>Efficient data transformation</p>
</li>
<li><p>Minimal API calls</p>
</li>
</ul>
</li>
</ol>
<p>This implementation ensures our game has a reliable source of images while handling potential API failures gracefully. The consistent data format across all APIs makes it easy to switch between them without affecting the game's functionality.</p>
<h2 id="heading-testing-the-app">Testing the App</h2>
<p>Testing is a crucial part of any application development, and for our Memory Card Game, we implemented a comprehensive testing strategy using modern tools and practices. Let's dive into how we structured our tests and some key testing patterns we used.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732517719141/8b034f36-761e-4433-acfd-82a2c4cffffc.png" alt="Running Tests" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h3 id="heading-testing-stack">Testing Stack</h3>
<ul>
<li><p><strong>Vitest</strong>: Our primary testing framework, chosen for its speed and seamless integration with Vite</p>
</li>
<li><p><strong>React Testing Library</strong>: For testing React components with a user-centric approach</p>
</li>
<li><p><strong>@testing-library/user-event</strong>: For simulating user interactions</p>
</li>
<li><p><strong>jsdom</strong>: For creating a DOM environment in our tests</p>
</li>
</ul>
<h3 id="heading-key-testing-patterns">Key Testing Patterns</h3>
<p>Testing was a crucial part of ensuring the reliability and maintainability of this Memory Card Game. I implemented a comprehensive testing strategy using React Testing Library and Vitest, focusing on several key areas:</p>
<h4 id="heading-1-component-testing">1. Component Testing</h4>
<p>I wrote extensive tests for my React components to ensure they render correctly and behave as expected. For example, the <code>CardsGrid</code> component, which is the heart of the game, has thorough test coverage including:</p>
<ul>
<li><p>Initial rendering states</p>
</li>
<li><p>Loading states</p>
</li>
<li><p>Error handling</p>
</li>
<li><p>Score tracking</p>
</li>
<li><p>Card interaction behavior</p>
</li>
</ul>
<h4 id="heading-2-test-mocking">2. Test Mocking</h4>
<p>To ensure reliable and fast tests, I implemented several mocking strategies:</p>
<ul>
<li><p>Local storage operations using useLocalStorage hook</p>
</li>
<li><p>API calls using the <code>useFetch</code> hook</p>
</li>
<li><p>Event handlers and state updates</p>
</li>
</ul>
<h4 id="heading-3-testing-best-practices">3. Testing Best Practices</h4>
<p>Throughout my testing implementation, I followed several best practices:</p>
<ul>
<li><p>Using <code>beforeEach</code> and <code>afterEach</code> hooks to reset state between tests</p>
</li>
<li><p>Testing user interactions using <code>fireEvent</code> from React Testing Library</p>
</li>
<li><p>Writing tests that resemble how users interact with the app</p>
</li>
<li><p>Testing both success and error scenarios</p>
</li>
<li><p>Isolating tests using proper mocking</p>
</li>
</ul>
<h4 id="heading-4-testing-tools">4. Testing Tools</h4>
<p>The project leverages modern testing tools and libraries:</p>
<ul>
<li><p><strong>Vitest</strong>: As the test runner</p>
</li>
<li><p><strong>React Testing Library</strong>: For testing React components</p>
</li>
<li><p><strong>@testing-library/jest-dom</strong>: For enhanced DOM testing assertions</p>
</li>
<li><p><strong>@testing-library/user-event</strong>: For simulating user interactions</p>
</li>
</ul>
<p>This comprehensive testing approach helped me catch bugs early, ensured code quality, and made refactoring safer and more manageable.</p>
<h2 id="heading-optimizations">Optimizations</h2>
<p>To ensure smooth performance, especially on mobile devices, we implemented several optimization techniques:</p>
<ol>
<li><p><strong>Response Transformation</strong></p>
<ul>
<li><p>Standardized data format across all APIs</p>
</li>
<li><p>Efficient ID extraction from URLs</p>
</li>
<li><p>Structured image metadata for quick access</p>
</li>
</ul>
</li>
<li><p><strong>Network Optimization</strong></p>
<ul>
<li><p>Using <code>no-cors</code> mode where appropriate to handle CORS issues efficiently</p>
</li>
<li><p>Error handling with specific status codes for better debugging</p>
</li>
<li><p>Consistent response structure across all API implementations</p>
</li>
</ul>
</li>
<li><p><strong>Mobile-First Considerations</strong></p>
<ul>
<li><p>Optimized image loading strategy</p>
</li>
<li><p>Efficient error handling to prevent unnecessary retries</p>
</li>
<li><p>Streamlined data transformation to reduce processing overhead</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-future-improvements"><strong>Future Improvements</strong></h2>
<p>There are a few ways that we could further improve this project:</p>
<ol>
<li><p><strong>API Response Caching</strong></p>
<ul>
<li><p>Implement local storage caching for frequently used images</p>
</li>
<li><p>Add cache invalidation strategy for fresh content</p>
</li>
<li><p>Implement progressive image loading</p>
</li>
</ul>
</li>
<li><p><strong>Performance Optimizations</strong></p>
<ul>
<li><p>Add image lazy loading for better initial load time</p>
</li>
<li><p>Implement request queuing for better bandwidth management</p>
</li>
<li><p>Add response compression for faster data transfer</p>
</li>
</ul>
</li>
<li><p><strong>Reliability Enhancements</strong></p>
<ul>
<li><p>Add API health checking before attempts</p>
</li>
<li><p>Implement retry mechanisms with exponential backoff</p>
</li>
<li><p>Add circuit breaker pattern for failing APIs</p>
</li>
</ul>
</li>
<li><p><strong>Analytics and Monitoring</strong></p>
<ul>
<li><p>Track API success rates</p>
</li>
<li><p>Monitor response times</p>
</li>
<li><p>Implement automatic API switching based on performance metrics</p>
</li>
</ul>
</li>
</ol>
<p>This robust implementation ensures that our game remains functional and performant even under adverse network conditions or API unavailability, while still maintaining room for future improvements and optimizations.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building this Memory Card Game has been more than just creating a fun, ad-free alternative for kids—it's been an exercise in implementing modern web development best practices while solving a real-world problem.</p>
<p>The project demonstrates how combining thoughtful architecture, robust testing, and reliable fallback mechanisms can result in a production-ready application that's both entertaining and educational.</p>
<h3 id="heading-key-takeaways">🗝️ Key Takeaways</h3>
<ol>
<li><p><strong>User-Centric Development</strong></p>
<ul>
<li><p>Started with a clear problem (ad-filled games affecting user experience)</p>
</li>
<li><p>Implemented features that enhance gameplay without interruptions</p>
</li>
<li><p>Maintained focus on performance and reliability across devices</p>
</li>
</ul>
</li>
<li><p><strong>Technical Excellence</strong></p>
<ul>
<li><p>Leveraged modern React patterns and hooks for clean, maintainable code</p>
</li>
<li><p>Implemented comprehensive testing strategy ensuring reliability</p>
</li>
<li><p>Created a robust API fallback system for uninterrupted gameplay</p>
</li>
</ul>
</li>
<li><p><strong>Performance First</strong></p>
<ul>
<li><p>Adopted mobile-first approach with responsive design</p>
</li>
<li><p>Optimized image loading and handling</p>
</li>
<li><p>Implemented efficient state management and caching strategies</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-learning-outcomes">📚 Learning Outcomes</h3>
<p>This project showcases how seemingly simple games can be excellent vehicles for implementing complex technical solutions. From component architecture to API fallbacks, each feature was built with scalability and maintainability in mind, proving that even hobby projects can maintain professional-grade code quality.</p>
<h3 id="heading-moving-forward">🔮 Moving Forward</h3>
<p>While the game successfully achieves its primary goal of providing an ad-free, enjoyable experience, the documented future improvements provide a clear roadmap for evolution. Whether it's implementing additional optimizations or adding new features, the foundation is solid and ready for expansion.</p>
<p>The Memory Card Game stands as a testament to how personal projects can both solve real-world problems and serve as platforms for implementing best practices in modern web development. Feel free to explore the <a target="_blank" href="https://github.com/mihailgaberov/memory-card">code</a>, contribute, or use it as inspiration for your own projects!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build Your Own Wheel of Names with React and TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ A while ago, I stumbled upon a website listing various coding challenges, and I decided to give some a try. Last week, I came across one that involved building a "Wheel of Names." It reminded me of a similar project I built years ago using Flash and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-your-own-wheel-of-names/</link>
                <guid isPermaLink="false">67190fa49a9d435e2ceeafb6</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Bun ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Wheel of Names ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Wed, 23 Oct 2024 15:00:52 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729695428229/56ac185a-bed1-4bbc-ab6d-a12f2ac5adee.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A while ago, I stumbled upon a website listing various coding challenges, and I decided to give some a try.</p>
<p>Last week, I came across one that involved building a "Wheel of Names." It reminded me of a similar project I built years ago using Flash and ActionScript 3—technologies that have since fallen out of use. So, I thought it would be fun to recreate the wheel, but this time using a modern tech stack.</p>
<p>In this tutorial, I’ll walk you through how I built it from scratch.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-description">Project Description</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-would-i-need-a-wheel-of-names">Why Would I Need a Wheel of Names?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-plan-for-the-app">The Plan for the App</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-application-features">Application Features</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-tech-stack">The Tech Stack</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-build-the-app">Let’s Build the App</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-project-structure">Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-the-components">How to Build the Components</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-deploy-the-app-to-vercel">How to Deploy the App to Vercel</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
</ul>
<h2 id="heading-project-description">Project Description</h2>
<p>This will be an app that I presume is inspired by the TV show Wheel of Fortune. In the TV show, contestants try to figure out a short phrase by guessing letters. If they guess correctly, the letter will be revealed. They spin the wheel to determine how much money each correct letter is worth.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729622283461/8fde5307-eadd-4b4a-8f39-fe8e47478bd3.png" alt="The wheel in Wheel of Fortune displaying the different dollar amounts/prizes contestants can get" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Wheel of Names is similar, but allows us to create a virtual wheel, putting our own names on it. We can then virtually spin it to determine a winner.</p>
<h3 id="heading-github-repo"><strong>GitHub Repo</strong></h3>
<p>If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names">here</a> 💁 is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names/blob/main/README.md">README</a> 🙌. <a target="_blank" href="https://wheel-of-names-three.vercel.app/">Here</a> you can see the live demo.</p>
<h2 id="heading-why-would-i-need-a-wheel-of-names">Why Would I Need a Wheel of Names?</h2>
<p>First of all, it’s a lot of fun to build your own! A practical, real-life use case would be for running lottery-style games where you need to pick a random winner.</p>
<p>For example, imagine you’re part of an agile team that holds retrospectives every two weeks, and you need to randomly choose a team member to lead each session. Just add everyone’s name to the participants list, spin the wheel, and let it decide for you! 🎡</p>
<h2 id="heading-the-plan-for-the-app">The Plan for the App</h2>
<p>The app is made up of several components, with the main feature being the spinning wheel. The wheel will have a section for each participant, and each section will be uniquely coloured, with its size proportionally calculated based on the number of participants. Once the spinning animation finishes, the winner will be revealed with a fun, confetti-style popup.</p>
<p>Other parts of the app include a section to enter the question or phrase that the spin is for, as well as controls for adding participant names and displaying them in a neatly organised list.</p>
<p>The list will offer options to sort and shuffle the names. The sorting will arrange the names alphabetically, while the shuffle option will randomise them. You can also delete any previously added participant.</p>
<p>All of these changes are dynamically reflected on the wheel component, ensuring that the wheel stays up-to-date with the latest participant list.</p>
<p>Here are a few screenshots that showcase what the app will look like once it's complete.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729578916704/0634a255-99a3-4a9c-8d64-6468cd732d40.png" alt="Wheel of Names app - initial empty state" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729578959007/33b8c9a5-73d0-4c61-bdad-1006107358a8.png" alt="Wheel of Names app - adding question" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729578989621/fa954e01-52be-4e60-8a01-27a7a2b12a70.png" alt="Wheel of Names app - adding participants" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>Here are some YouTube videos I recorded after completing the app, showcasing its features in action.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/sugUnci1Rlw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/gIc6wtH9fK8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h3 id="heading-application-features"><strong>Application features:</strong></h3>
<p><strong>I. Question</strong></p>
<ol>
<li><p>This is where users can submit a question or phrase that will determine the focus of the spins.</p>
</li>
<li><p>Any changes made in the input field are saved when the user clicks outside of it (on focus out).</p>
</li>
</ol>
<p><strong>II. Wheel</strong></p>
<ol>
<li><p>The wheel component spins with an easing animation and determines the winner.</p>
</li>
<li><p>The spin direction can be adjusted using the buttons, for either clockwise or counterclockwise rotation.</p>
</li>
<li><p>Each adjacent sector is uniquely coloured, and their sizes are calculated proportionally to the number of participants.</p>
</li>
</ol>
<p><strong>III. Add Participants</strong></p>
<ol>
<li><p>The participant entry area includes an input field for entering a participant's name and an 'ADD' button to add it to the participants list.</p>
</li>
<li><p>To add participants more quickly, the user can press the ENTER key on the keyboard.</p>
</li>
</ol>
<p><strong>IV. Participants List</strong></p>
<ol>
<li><p>This section displays all the participants' names.</p>
</li>
<li><p>The list offers options to sort the names alphabetically or shuffle them randomly, with both actions dynamically updating the wheel component.</p>
</li>
</ol>
<h3 id="heading-the-tech-stack">The Tech Stack</h3>
<p>Here’s a list of the main technologies we’ll be using:</p>
<ul>
<li><p><strong>Bun</strong> – A fast JavaScript bundler and package manager, known for its speed and simplicity.</p>
</li>
<li><p><strong>Vite</strong> – A build tool that provides a fast development environment, particularly optimised for modern web projects.</p>
</li>
<li><p><strong>React</strong> – A popular JavaScript library for building user interfaces, enabling efficient rendering and state management.</p>
</li>
<li><p><strong>TypeScript</strong> – A superset of JavaScript that adds static typing, improving code quality and maintainability.</p>
</li>
<li><p><strong>styled-components</strong> – A library for writing CSS-in-JS, allowing styles to be scoped to components and providing a more dynamic approach to styling.</p>
</li>
<li><p><strong>canvas</strong> – A powerful HTML element used to draw graphics, animations, and other dynamic content directly on the web page.</p>
</li>
<li><p><strong>canvas-confetti</strong> – A JavaScript library for adding fun, celebratory confetti animations to the canvas, perfect for announcing winners.</p>
</li>
</ul>
<h2 id="heading-lets-build-the-app"><strong>Let’s Build the App</strong></h2>
<p>From this point onward I will guide you through the process I followed when building this app.</p>
<h3 id="heading-project-structure"><strong>Project Structure</strong></h3>
<p>The project structure is quite straightforward, thanks to React and styled-components, which make this modular approach easy to implement. You can check out the project structure in my GitHub repo.</p>
<p>Below, I’ll walk you through the reasoning behind the structure and explain the decisions I made for each part.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729579163492/2b406e15-c8f6-4533-9a93-4c6e7b1435f4.png" alt="Project Structure" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<ul>
<li><p><strong>main.tsx</strong>: The entry point of the React app created with Vite.</p>
</li>
<li><p><strong>App.tsx</strong>: The parent component that includes all other components and handles participant name management (adding, removing, sorting, shuffling).</p>
</li>
<li><p><strong>Header.tsx</strong>: Top part of the app, which renders the app title.</p>
</li>
<li><p><strong>Participants.tsx</strong>: Renders the controls for adding and displaying participants. It includes a validation function to prevent empty or invalid names.</p>
</li>
<li><p><strong>Question.tsx</strong>: Displays the question section, managing state and basic keyboard and click functionality.</p>
</li>
<li><p><strong>Wheel.tsx</strong>: The core component containing the animation logic, sector size/colouring, and rendering participant names. It uses the <code>canvas</code> element for smooth spinning and integrates <code>confetti</code> to announce the winner.</p>
</li>
<li><p><strong>utils.ts</strong>: A file with helper functions used across the app.</p>
</li>
<li><p><strong>styles.ts</strong>: Contains shared styled components, exported for use throughout the app.</p>
</li>
</ul>
<h4 id="heading-css-files-and-configs">CSS Files and Configs</h4>
<p>The remaining files in the project include standard boilerplate CSS styles from the initial Vite setup, along with configuration files for Vite, TypeScript, Prettier, and ESLint. These configurations are commonly used in modern projects and are not specific to this app, so I won't dive into them here. You can easily find documentation for each online.</p>
<h3 id="heading-how-to-build-the-components">How to Build the Components</h3>
<p>In this section, we will go through the process of building each component of the application, step by step. By the end, you’ll have a fully functional app with modular, self-contained components.</p>
<h4 id="heading-1-app-component">1. App component</h4>
<p>The App component serves as the central container for the entire application. It encapsulates all the core building blocks and is responsible for managing the state of the participants' names. Beyond rendering the UI, it handles key application logic, such as adding, removing, sorting, and shuffling participants.</p>
<p>The component uses local state to hold the list of names. This state is updated through callback functions that are triggered by interactions in the child components — specifically, the <code>Participants</code> and <code>Wheel</code> components.</p>
<p>The primary handler functions, <code>handleAddName</code> and <code>handleRemoveName</code>, manage adding and removing names from the list. Additionally, there are two other handlers dedicated to manipulating the order of the names: one for sorting (<code>handleSortNames</code>) and one for shuffling (<code>handleShuffleNames</code>). These handlers provide flexibility in how the list of participants is displayed and interacted with in the app.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [names, setNames] = useState&lt;<span class="hljs-built_in">string</span>[]&gt;([]);

  <span class="hljs-keyword">const</span> handleAddName = <span class="hljs-function">(<span class="hljs-params">name: <span class="hljs-built_in">string</span></span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (names.length &lt; MAX_PARTICIPANTS) {
      setNames([...names, name]);
    }
  };

  <span class="hljs-keyword">const</span> handleRemoveName = <span class="hljs-function">(<span class="hljs-params">index: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
    setNames(names.filter(<span class="hljs-function">(<span class="hljs-params">_, i</span>) =&gt;</span> i !== index));
  };

  <span class="hljs-keyword">const</span> shuffleNames = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> shuffledNames = [...names].sort(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Math</span>.random() - <span class="hljs-number">0.5</span>);
    setNames(shuffledNames);
  };

  <span class="hljs-keyword">const</span> sortNames = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> sortedNames = [...names].sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a.localeCompare(b));
    setNames(sortedNames);
  };
</code></pre>
<p>A crucial part of the component is the <code>MAX_PARTICIPANTS</code> constant, which sets a limit on the number of participants allowed. This ensures that the app doesn't exceed a certain number of entries, maintaining performance and usability.</p>
<p>The rendering structure of this component looks as follows:</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;Header /&gt;
      &lt;Question /&gt;
      &lt;Main&gt;
        &lt;Participants
          handleAddName={handleAddName}
          handleRemoveName={handleRemoveName}
          shuffleNames={shuffleNames}
          sortNames={sortNames}
          names={names}
        /&gt;
        &lt;Wheel participants={names} /&gt;
      &lt;/Main&gt;
    &lt;/&gt;
  );
</code></pre>
<h4 id="heading-2-header-component">2. Header component</h4>
<p>The <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names/blob/main/src/Header.tsx">Header component</a> is the simplest part of the application. Its primary role is to display the title at the top of the page. This component is essential for setting the tone and branding of the application. Despite its simplicity, it lays the foundation for structuring the UI and can be easily customised or extended in the future.</p>
<p>Here is how it looks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729581820870/9384834a-8657-435d-89f5-e404a67d6ac0.png" alt="Header Component" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-3-question-component">3. Question component</h4>
<p>The <a target="_blank" href="https://github.com/mihailgaberov/Wheel-of-Names/blob/main/src/Question.tsx">component</a> that displays the input for entering a question or phrase is relatively simple. It renders a text field and utilises a few handler functions to enhance the user experience. These handlers manage focus behaviour: setting focus when the input field is clicked, removing focus when the user clicks outside the field, and allowing the user to use the ENTER or ESCAPE keys to submit or cancel their input, respectively.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729581877713/94fa416b-48e0-4a33-8ed4-1db975ce7542.png" alt="Question Component on Focus" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<h4 id="heading-4-participants">4. Participants</h4>
<p>In this part of the app, we render the list of all added participants. The component includes a local validation function that runs each time before adding a new participant, ensuring that the input meets the necessary criteria (for example, no duplicates or empty names).</p>
<p>We also leverage built-in HTML attributes to dynamically enable or disable buttons based on the state of the participants list. For example, the "Sort" and "Shuffle" buttons are disabled when the list is empty, while the "Add" button is disabled once the maximum participant limit (<code>MAX_PARTICIPANTS</code>) has been reached. This ensures a smooth and intuitive user experience by preventing invalid actions.</p>
<p>You’ve probably already noticed how we use a utility function from the <code>utils.ts</code> file to capitalise the participant names before displaying them. This ensures that all names are presented in a consistent and user-friendly format.</p>
<p>This happens inside a <code>map()</code> loop, where we iterate over the <code>names</code> data structure and display each participant's name in a separate row within the list component. The utility function is applied during this iteration to ensure that the names are properly capitalised before rendering.</p>
<h4 id="heading-5-wheel-component">5. Wheel component</h4>
<p>This is the largest component in our app. At the top, you'll find the styles required to position the winning popup, which is accompanied by confetti when a winner is selected. Below that, we define an array containing all the possible colours used to color the wheel sectors. Afterward, we move into the component code itself.</p>
<p>The component utilises several states to ensure the spinning animation behaves as expected. Additionally, it manages when to trigger and display the winner popup, with the winner's name shown inside. These states and handlers work together to create a smooth and interactive experience.</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> [spinning, setSpinning] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [rotation, setRotation] = useState(<span class="hljs-number">0</span>);
  <span class="hljs-keyword">const</span> [spinDirection, setSpinDirection] = useState&lt;
    <span class="hljs-string">'clockwise'</span> | <span class="hljs-string">'counterclockwise'</span>
  &gt;(<span class="hljs-string">'clockwise'</span>);
  <span class="hljs-keyword">const</span> [showPopup, setShowPopup] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [popupWinner, setPopupWinner] = useState&lt;<span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
</code></pre>
<p>The <code>drawWheel()</code> method is responsible for rendering the wheel with the specified number of sectors on the canvas. This method relies heavily on the <code>canvas</code> element and its associated API to draw each sector and participant's name. We also use our helper function to capitalise the participants' names in the wheel, ensuring consistency with the list component.</p>
<p>When the "Spin" button is clicked, the <code>startSpin()</code> method is triggered. This is where the animation logic is implemented. We generate a random number of rotations, ranging from 5 to 10 full rotations, to make the spin feel unpredictable.</p>
<p>The direction of the spin is determined by the user's selection, allowing the wheel to spin either clockwise or counterclockwise. We also set the spin duration to 6000ms (6 seconds) for a smooth and engaging animation.</p>
<p>To enhance the realism of the animation, we apply an easing function that implements the "Ease-out cubic" effect, which causes the wheel to gradually slow down as it reaches the end of the spin.</p>
<pre><code class="lang-typescript"> <span class="hljs-keyword">const</span> easing = <span class="hljs-function">(<span class="hljs-params">t: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
      <span class="hljs-comment">// Ease-out cubic</span>
      <span class="hljs-keyword">return</span> <span class="hljs-number">1</span> - <span class="hljs-built_in">Math</span>.pow(<span class="hljs-number">1</span> - t, <span class="hljs-number">3</span>);
    };
</code></pre>
<p>The animation is handled by an inner function called <code>animate()</code>, which utilises the <code>requestAnimationFrame</code> API, a feature supported by all modern browsers for smooth, high-performance animations. Inside this function, we calculate the elapsed time and the current rotation, updating the component's state accordingly to ensure the wheel spins smoothly.</p>
<p>During each animation frame, we also invoke the <code>determineWinner()</code> function, which is defined below. This function calculates the winning sector by determining which sector the wheel lands on at the end of the spin. It then updates the popup state to display the winner’s name inside the popup.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> determineWinner = <span class="hljs-function">(<span class="hljs-params">finalRotation: <span class="hljs-built_in">number</span></span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> sliceAngle = <span class="hljs-number">360</span> / numSectors;
    <span class="hljs-keyword">const</span> normalizedRotation = ((finalRotation % <span class="hljs-number">360</span>) + <span class="hljs-number">360</span>) % <span class="hljs-number">360</span>;
    <span class="hljs-keyword">const</span> winningSector = <span class="hljs-built_in">Math</span>.floor(normalizedRotation / sliceAngle);

    setPopupWinner(participants[winningSector]);
    setShowPopup(<span class="hljs-literal">true</span>);
  };
</code></pre>
<p>Changing the direction of the spin is straightforward. We simply update the component’s state based on the value of the button’s label, which toggles between "Clockwise" and "Counterclockwise." By setting the state accordingly, we can easily control the spin direction with a single click of the button.</p>
<p>The remaining code before the rendering part of this component includes an effect that controls the visibility of the confetti popup. The <code>startConfetti</code> function is responsible for initiating the confetti animation when a winner is selected. This effect ensures that the confetti animation is triggered and displayed at the right moment, adding a celebratory touch to the experience.</p>
<p>And with all that we are ready to render our Wheel component as it follows:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;canvas
        ref={canvasRef}
        width={<span class="hljs-number">400</span>}
        height={<span class="hljs-number">400</span>}
        style={{ borderRadius: <span class="hljs-string">'50%'</span>, border: <span class="hljs-string">'2px solid black'</span> }}
      /&gt;
      &lt;ButtonsContainer&gt;
        &lt;Button
          onClick={changeSpinDirection}
          disabled={participants.length === <span class="hljs-number">0</span> || spinning}
        &gt;
          {capitalize(spinDirection)}
        &lt;/Button&gt;
        &lt;Button
          onClick={startSpin}
          disabled={participants.length === <span class="hljs-number">0</span> || spinning}
        &gt;
          Spin
        &lt;/Button&gt;
      &lt;/ButtonsContainer&gt;
      {showPopup &amp;&amp; popupWinner &amp;&amp; (
        &lt;Popup&gt;
          &lt;h2&gt;Congratulations!&lt;/h2&gt;
          &lt;h3&gt;{capitalize(popupWinner)}&lt;/h3&gt;
        &lt;/Popup&gt;
      )}
    &lt;/div&gt;
</code></pre>
<h2 id="heading-how-to-deploy-the-app-to-vercel"><strong>How to Deploy the App to Vercel</strong></h2>
<p>Finally 🎉 we’re ready to deploy our app.</p>
<p>I used Vercel for this deployment because it offers a fast, free, and easy way to deploy web apps. If you'd like a more detailed guide on how to deploy with Vercel, check out my <a target="_blank" href="https://www.mihailgaberov.com/build-a-real-time-order-book-application-with-react-and-websockets">previous tutorial</a> for step-by-step instructions.</p>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>I hope you found this process as interesting and enjoyable to follow as it was for me to create!</p>
<p>Now, let’s take a moment to reflect on what we’ve accomplished and highlight a few key takeaways that could prove useful for future projects.</p>
<ol>
<li><p><strong>Modular Design</strong>: Breaking the app into small, manageable components made it easier to maintain and scale.</p>
</li>
<li><p><strong>React and Styled-Components</strong>: These tools streamlined the development, allowing for dynamic styling and efficient UI management.</p>
</li>
<li><p><strong>Canvas for Animations</strong>: Leveraging the <code>canvas</code> element enabled smooth and visually appealing animations.</p>
</li>
<li><p><strong>Vercel Deployment</strong>: Vercel's simplicity and speed made it the ideal choice for quickly deploying the app.</p>
</li>
</ol>
<p>This project highlighted the power of modern tools like React, TypeScript, and canvas, all while ensuring the app stayed modular and easy to maintain.</p>
<p>Thanks for reading! 🙏</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Shopping Cart with React and TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial we are going to build a real-life shopping cart 🛒 application. We'll talk about the technology stack and the features it will have in a minute. I'll also walk you through the process step-by-step. But first, let me show you what it'... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-shopping-cart-with-react-and-typescript/</link>
                <guid isPermaLink="false">66d460483a8352b6c5a2aab4</guid>
                
                    <category>
                        <![CDATA[ ecommerce ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Fri, 23 Jun 2023 17:09:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/shopping-cart-app-article.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial we are going to build a real-life shopping cart 🛒 application.</p>
<p>We'll talk about the technology stack and the features it will have in a minute. I'll also walk you through the process step-by-step. But first, let me show you what it's going to look like.</p>
<h2 id="heading-lets-sketch">Let’s sketch 🙃</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-214.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Product List</em></p>
<p>We are going to make our app mobile friendly by implementing a decent level of responsiveness.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-215.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Product List - Mobile</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-216.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cart Desktop</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-217.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cart Mobile</em></p>
<p>This time, before going directly into building this project, I decided to go more traditional. I took a pen and paper and drew these ugly sketches, just to make sure I had a visual idea of what I would be building.</p>
<p>And honestly, it really did the trick ✨. This technique helps when you are about to sit at your computer and ask the question:</p>
<blockquote>
<p>What do I start with now?</p>
</blockquote>
<h3 id="heading-tldr"><strong>TL;DR</strong></h3>
<p>💡 If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app">here</a> 💁 is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/blob/main/README.md">README</a> 🙌, and here you can see the live <a target="_blank" href="https://shopping-cart-app-coral.vercel.app/">demo</a>.</p>
<h2 id="heading-whats-a-shopping-cart">What’s a Shopping Cart?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-218.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Shopping Carts</em></p>
<p>A shopping cart lets people carry or store their goods while they're shopping – either online or in real life.</p>
<p>In ecommerce apps, the shopping cart is a place where the user can store and view the items they are considering purchasing. Typically, this is a separate page or part of the page where people can view a list of the items they have chosen to buy before actually paying for them.</p>
<h2 id="heading-the-plan-for-our-app">The Plan for Our App</h2>
<p>We will build an application consisting of two pages: a product list page and a cart page.</p>
<p>The app will fetch the data from a 3rd party RESTful API and it'll use the browser’s localStorage to store selected items that should be displayed in the cart.</p>
<h3 id="heading-application-features">Application features</h3>
<p>The shopping cart application must fetch and display products from the API endpoint <a target="_blank" href="https://dummyjson.com/products">https://dummyjson.com/products</a>.</p>
<p>The Products List page should display the available items along with some specific information. For example, it should show 3 products per row for large viewports. Each item should display at least a thumbnail image, a title, the price (formatted as GBP, for example £100.23), and an “Add to Cart” button which adds the item to the cart.</p>
<p>Cart page should display the customer's chosen items. Each item should display at least a thumbnail image, a title, plus and minus buttons (for adding/removing items) and the current quantity of the item in the cart, like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-219.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Plus and minus buttons</em></p>
<p>If there is a quantity of 1 item in the cart, hitting minus removes it from the cart. The cart should also display the total price of all items added (formatted as GBP, for example, £100.23).</p>
<p>In addition to these basic features, the UI/UX should be as sleek as possible. We also want to make sure we unit test the application.</p>
<h3 id="heading-tech-stack">Tech Stack</h3>
<p>If you've had the chance to take a look at some of my others tutorials, the tech stack I chose won’t surprise you much.</p>
<p>I picked these technologies taking into account the requirements for the application – that it would be performant, well tested, and have a sleek look and feel.</p>
<ul>
<li><p>React / TypeScript / Vite – for the UI library we're going again with a React and <a target="_blank" href="https://cloudfour.com/thinks/in-praise-of-vite/">Vite</a> development environment. But this time we will use it with TypeScript instead of JavaScript.</p>
</li>
<li><p>SASS / CSS Modules – for styling our app, we'll bet on the battle-tested solution of <a target="_blank" href="https://github.com/css-modules/css-modules">CSS Modules</a> with <a target="_blank" href="https://sass-lang.com/documentation/">SASS/SCSS</a>.</p>
</li>
<li><p>react-testing-library / Vitest – for testing the app, we'll use <a target="_blank" href="https://testing-library.com/docs/react-testing-library/intro/">react-testing-library</a> and <a target="_blank" href="https://vitest.dev/guide/">Vitest</a>.</p>
</li>
</ul>
<p>If you want to learn more about RTL, here is an insightful <a target="_blank" href="https://www.robinwieruch.de/react-testing-library/">tutorial</a> by a very knowledgable <a target="_blank" href="https://www.robinwieruch.de/about/">guy</a> that can help you out.</p>
<h2 id="heading-how-to-build-the-app">How to Build the App</h2>
<p>In this section we will take a look at the project structure and I'll explain why I chose it.</p>
<p>Then I'll go briefly through each of the components and describe its role.</p>
<p>Once you understand how the components work together to create a functioning application, we'll explore how to utilize the browser's local storage to store data that can be used in other parts of the app.</p>
<h3 id="heading-dependencies"><strong>📦 Dependencies</strong></h3>
<p>Let's take a brief look at our project's dependencies. These are external packages we need to install in order to ensure the successful execution of our project.</p>
<p>Along with Vite and Vitest, I have installed SASS, React Testing Library, and use-local-storage-state. See below my <a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/blob/main/package.json">package.json</a> file.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"shopping-cart-app"</span>,
  <span class="hljs-attr">"private"</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Mihail Gaberov"</span>,
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"vitest"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc &amp;&amp; vite build"</span>,
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0"</span>,
    <span class="hljs-attr">"preview"</span>: <span class="hljs-string">"vite preview"</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-attr">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-attr">"react-router-dom"</span>: <span class="hljs-string">"^6.13.0"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@testing-library/jest-dom"</span>: <span class="hljs-string">"^5.16.5"</span>,
    <span class="hljs-attr">"@testing-library/react"</span>: <span class="hljs-string">"^14.0.0"</span>,
    <span class="hljs-attr">"@types/react"</span>: <span class="hljs-string">"^18.0.37"</span>,
    <span class="hljs-attr">"@types/react-dom"</span>: <span class="hljs-string">"^18.0.11"</span>,
    <span class="hljs-attr">"@typescript-eslint/eslint-plugin"</span>: <span class="hljs-string">"^5.59.0"</span>,
    <span class="hljs-attr">"@typescript-eslint/parser"</span>: <span class="hljs-string">"^5.59.0"</span>,
    <span class="hljs-attr">"@vitejs/plugin-react"</span>: <span class="hljs-string">"^4.0.0"</span>,
    <span class="hljs-attr">"eslint"</span>: <span class="hljs-string">"^8.38.0"</span>,
    <span class="hljs-attr">"eslint-plugin-react-hooks"</span>: <span class="hljs-string">"^4.6.0"</span>,
    <span class="hljs-attr">"eslint-plugin-react-refresh"</span>: <span class="hljs-string">"^0.3.4"</span>,
    <span class="hljs-attr">"jsdom"</span>: <span class="hljs-string">"^22.1.0"</span>,
    <span class="hljs-attr">"sass"</span>: <span class="hljs-string">"^1.63.4"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^5.0.2"</span>,
    <span class="hljs-attr">"use-local-storage-state"</span>: <span class="hljs-string">"^18.3.3"</span>,
    <span class="hljs-attr">"vite"</span>: <span class="hljs-string">"^4.3.9"</span>,
    <span class="hljs-attr">"vitest"</span>: <span class="hljs-string">"^0.32.0"</span>
  }
}
</code></pre>
<h3 id="heading-installation"><strong>🧑🏻‍💻 Installation</strong></h3>
<p>In this step I assume you are starting from scratch. We are going to use Vite for scaffolding the project. In order to do that you need to have Node.js installed on your system – at least version 14.18. I suggest that you update it to the latest stable version. And as a package manager you may go with either <a target="_blank" href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm">npm</a> or <a target="_blank" href="https://classic.yarnpkg.com/lang/en/docs/install/">yarn</a>.</p>
<p>Once you have one of these installed on your system, open your Terminal app and run the following:</p>
<pre><code class="lang-bash">yarn create vite your-app-name --template react-ts
</code></pre>
<p>This command will install the initial application files in a folder named 'your-app-name'. After that step you will be able to open it in your favorite IDE and start working on it.</p>
<p>One last thing you should do here is to install to additional packages I mentioned in the previous section. You can do that by running the following:</p>
<pre><code class="lang-bash">yarn add -D sass @testing-library/react use-local-storage-state
</code></pre>
<h3 id="heading-project-structure"><strong>🏗️ Project Structure</strong></h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-220.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Project Structure</em></p>
<p>This won't be much of a surprise for those of you who have some experience in building React applications. The structure I've chosen is pretty standard.</p>
<p>The root level of the app contains files related to configurations and setup, as well as the HTML index file. This is where the main JavaScript module is loaded and the app is launched.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-221.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>index.html</em></p>
<p>The <code>src</code> folder (short for "source") contains two sub-folders: one for assets and one for components.</p>
<p>The <code>public</code> and <code>screenshots</code> folders have straightforward purposes. The <code>.github</code> folder contains the YAML configuration file that is used by <a target="_blank" href="https://github.com/features/actions">GitHub Actions</a>. We will discuss this in more detail later on.</p>
<h3 id="heading-components"><strong>🛠️</strong>Components</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-222.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>App Components</em></p>
<p>All components are organized into separate folders. Within each folder, you'll find an index.ts file that exports the component. This file uses <a target="_blank" href="https://react.dev/learn/importing-and-exporting-components#default-vs-named-exports">named exports</a>, as shown below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-223.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Named export - Header component</em></p>
<p>We'll start by examining the components from a top-to-bottom approach, as they are seen and used within the application. To help clarify, let me provide a visualization.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-224.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>App Components Visualized in Order</em></p>
<p>Here's what each of these components does in more detail:</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/Header">Header</a> – holds the top part of the app. On the left side is the logo image, an SVG I have downloaded from <a target="_blank" href="https://icon-sets.iconify.design/noto-v1/shopping-bags/">Iconify</a>. On the right side sits the CartWidget component.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/CartWidget">CartWidget</a> – renders a button composed of an SVG image depicting a shopping cart and a number value indicating the count of products currently added to the cart. When clicked, the button takes the user to the cart page.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/Products">Products</a> – this component is responsible for rendering the main content of the page, which consists of a list of products. On larger viewports, the products are displayed in three columns per row. Each product is represented by a thumbnail image, a title, price information, and an "Add to Cart" button. The price of each product is formatted to GBP using the CurrencyFormatter component.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/CurrencyFormatter">CurrencyFormatter</a> – formats given numeric amount to GBP – that is, 499 would become £499.00.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-225.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>App Components Visualized</em></p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/Cart">Cart</a> – this component is responsible for rendering the main content of the page. It displays one product per row and includes a quantifier component that allows the user to update the quantity of the product. At the bottom of the page, it also shows the total price of the selected products, which is formatted as GBP using the CurrencyFormatter component.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/Quantifier">Quantifier</a> – this component displays plus and minus buttons along with an input field positioned between them. It serves the purpose of indicating the current quantity of a product and enables the user to modify this value. Additionally, it offers functionality to remove the product entirely from the shopping cart.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/Footer">Footer</a> – this component is designed to provide a simple and visually representative way to display information about the author and copyrights.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/Loader">Loader</a> – this component is not visible on the screenshots above, but it represents a simple loading animation that becomes visible once the user opens the app for first time and the products data is still being loaded.</p>
<h3 id="heading-how-to-build-the-header">🧩 How to Build the Header</h3>
<p>As mentioned earlier, the upper section of the application, commonly referred to as the 'hat', is known as the header. In our specific case, the header comprises two elements: the logo positioned on the left and the CartWidget on the right.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-22-at-09.54.02.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Application Header</em></p>
<p>Now, let's go through the process of constructing the application together 🙌. The steps outlined below are applicable to every component we incorporate into the app.</p>
<p>To begin, I create a dedicated folder for the component and include an index.ts file within it. This file will serve as the export module for the component.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-22-at-09.56.31.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Creating Header Component</em></p>
<p>This is where we export the actual component like that:</p>
<pre><code class="lang-python">export { Header } <span class="hljs-keyword">from</span><span class="hljs-string">'./Header'</span>
</code></pre>
<p>Then we implement the component itself. This code will go into a file named the same thing – <code>Header.tsx</code></p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> { FunctionComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>


export const Header: FunctionComponent = () =&gt; {

  <span class="hljs-keyword">return</span> (
    &lt;header&gt;
      header content here...
    &lt;/header&gt;
  )
}
</code></pre>
<p>We're starting simple.</p>
<p>Currently, this component only displays the text 'header content here...' on the page. Our goal is to gradually enhance it until we achieve the final result depicted in the picture above.</p>
<p>To do this, it is important to incorporate styling into the process. By utilizing CSS Modules, we can import a separate SCSS file containing the styles required for our component.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FunctionComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">'./header.module.scss'</span> <span class="hljs-comment">// &lt;---- imports the styles</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Header: FunctionComponent = <span class="hljs-function">() =&gt;</span> {

  <span class="hljs-keyword">return</span> (
    &lt;header&gt;
      header content here...
    &lt;/header&gt;
  )
}
</code></pre>
<p>This file must exist in our component's folder. After including the tests file, the folder structure for this component will resemble the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-22-at-10.16.21.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Header Component's Folder</em></p>
<p>Let's enhance the component's code by incorporating the necessary elements. On the left side, we'll add the logo element, which will function as a clickable link. We'll also include the <code>CartWidget</code> component that displays the count of the selected products.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FunctionComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Header: FunctionComponent = <span class="hljs-function">() =&gt;</span> {

  <span class="hljs-keyword">return</span> (
    &lt;header className={classes.header}&gt;
      &lt;div&gt;
        &lt;Link to=<span class="hljs-string">"/"</span>&gt;
          &lt;img src={logo} className={classes.logo} alt=<span class="hljs-string">"Shopping Cart Application"</span> /&gt;
        &lt;/Link&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;CartWidget productsCount={productsCount} /&gt;
      &lt;/div&gt;
    &lt;/header&gt;
  )
}
</code></pre>
<p>To achieve a nice look and feel, and a decent level of responsiveness, we'll use the following styles:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.header</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#213547</span>;
  <span class="hljs-attribute">transition</span>: height <span class="hljs-number">0.3s</span> ease;
  <span class="hljs-attribute">position</span>: fixed;
  <span class="hljs-attribute">right</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0.9</span>;
  backdrop-<span class="hljs-attribute">filter</span>: saturate(<span class="hljs-number">180%</span>) blur(<span class="hljs-number">20px</span>);
  <span class="hljs-attribute">justify-content</span>: space-between;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">1</span>;

  <span class="hljs-selector-class">.logo</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">6em</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5em</span>;
    will-change: filter;
    <span class="hljs-attribute">transition</span>: filter <span class="hljs-number">300ms</span>;
    <span class="hljs-attribute">transform</span>: scaleX(-<span class="hljs-number">1</span>);


    &amp;<span class="hljs-selector-pseudo">:hover</span> {
      <span class="hljs-attribute">filter</span>: drop-shadow(<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">2em</span> <span class="hljs-number">#646cffaa</span>);
    }
  }
}
</code></pre>
<p>I will show you the code for the widget component below, but before that I want you to notice how we pass the products count value a prop. This way we can free ourselves from implementing any logic in the component itself and use it only for representational purposes.</p>
<p>That being said, here is the code of the component:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> { FunctionComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { useNavigate } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>

<span class="hljs-keyword">import</span> shoppingCart <span class="hljs-keyword">from</span> <span class="hljs-string">'../../assets/shopping-cart.svg'</span>
<span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">'./cart-widget.module.scss'</span>

interface Props {
  productsCount: number
}

export const CartWidget: FunctionComponent&lt;Props&gt; = ({ productsCount }) =&gt; {
  const navigate = useNavigate()

  const navigateToCart = () =&gt; {
    navigate(<span class="hljs-string">'/cart'</span>)
  }

  <span class="hljs-keyword">return</span> (
    &lt;button className={classes.container} onClick={navigateToCart}&gt;
      &lt;span className={classes.productsCount}&gt;{productsCount}&lt;/span&gt;
      &lt;img src={shoppingCart} className={classes.shoppingCart} alt=<span class="hljs-string">"Go to Cart"</span> /&gt;
    &lt;/button&gt;
  )
}
</code></pre>
<p>And its styling:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span> <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">background</span>: none;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">flex-direction</span>: row-reverse;
  <span class="hljs-attribute">justify-content</span>: space-between;

  &amp;<span class="hljs-selector-pseudo">:hover</span> {
    <span class="hljs-attribute">outline</span>: <span class="hljs-number">1px</span> solid white;
  }


  <span class="hljs-selector-class">.shoppingCart</span> {
    <span class="hljs-attribute">height</span>: <span class="hljs-number">3em</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5rem</span> .<span class="hljs-number">4rem</span>;
    will-change: filter;
    <span class="hljs-attribute">transition</span>: filter <span class="hljs-number">300ms</span>;
  }

  <span class="hljs-selector-class">.productsCount</span> {
    <span class="hljs-attribute">z-index</span>: <span class="hljs-number">1</span>;
    <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2em</span>;
    <span class="hljs-attribute">top</span>: <span class="hljs-number">38px</span>;
    <span class="hljs-attribute">color</span>: orange;
  }
}
</code></pre>
<h4 id="heading-header-shrinking">Header Shrinking</h4>
<p>Before we proceed, there is one more aspect to discuss: the smooth shrinking animation of the header that you can see while scrolling down.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screen-Recording-2023-06-22-at-10.50.40.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Header Shrinking</em></p>
<p>To accomplish this, I used React hooks along with a technique involving manual manipulation of styles for various DOM elements.</p>
<p>I implemented this functionality within a component method called <code>shrinkHeader</code>, which is invoked whenever a user scrolls. Within this method, I check if the current vertical scroll position exceeds a specified threshold value, <code>DISTANCE_FROM_TOP</code>, and accordingly apply different styles based on the outcome of this comparison.</p>
<p>One aspect we haven't discussed yet is using the hook for managing local storage, which we'll talk about later.</p>
<p>Here is the completed version of the component:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> { FunctionComponent, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>
<span class="hljs-keyword">import</span> useLocalStorageState <span class="hljs-keyword">from</span> <span class="hljs-string">'use-local-storage-state'</span>

<span class="hljs-keyword">import</span> logo <span class="hljs-keyword">from</span> <span class="hljs-string">'/logo.svg'</span>
<span class="hljs-keyword">import</span> { CartWidget } <span class="hljs-keyword">from</span> <span class="hljs-string">'../CartWidget'</span>
<span class="hljs-keyword">import</span> { CartProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'../Products/Products.tsx'</span>
<span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">'./header.module.scss'</span>

export const Header: FunctionComponent = () =&gt; {
  useEffect(() =&gt; {
    window.addEventListener(<span class="hljs-string">"scroll"</span>, () =&gt; shrinkHeader(), false)

    <span class="hljs-keyword">return</span> () =&gt; {
      window.removeEventListener(<span class="hljs-string">"scroll"</span>, () =&gt; shrinkHeader())
    }
  }, [])

  const shrinkHeader = () =&gt; {
    const DISTANCE_FROM_TOP = <span class="hljs-number">140</span>
    const headerElement = document.querySelector(<span class="hljs-string">"header"</span>) <span class="hljs-keyword">as</span> HTMLElement
    const logoElement = document.querySelectorAll(<span class="hljs-string">"img"</span>)[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> HTMLElement
    const cartWidgetElement = document.querySelectorAll(<span class="hljs-string">"img"</span>)[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> HTMLElement
    const productsCountElement = document.querySelector(<span class="hljs-string">"span"</span>) <span class="hljs-keyword">as</span> HTMLElement
    const scrollY = document.body.scrollTop || document.documentElement.scrollTop

    <span class="hljs-keyword">if</span> (scrollY &gt; DISTANCE_FROM_TOP) {
      headerElement.style.transition = <span class="hljs-string">"height 200ms ease-in"</span>
      headerElement.style.height = <span class="hljs-string">"80px"</span>
      logoElement.style.transition = <span class="hljs-string">"height 200ms ease-in"</span>
      logoElement.style.height = <span class="hljs-string">"4rem"</span>
      cartWidgetElement.style.transition = <span class="hljs-string">"height 200ms ease-in"</span>
      cartWidgetElement.style.height = <span class="hljs-string">"2rem"</span>
      productsCountElement.style.transition = <span class="hljs-string">"font-size 200ms ease-in"</span>
      productsCountElement.style.fontSize = <span class="hljs-string">"1.5em"</span>
    } <span class="hljs-keyword">else</span> {
      headerElement.style.height = <span class="hljs-string">"150px"</span>
      logoElement.style.height = <span class="hljs-string">"6rem"</span>
      cartWidgetElement.style.height = <span class="hljs-string">"3rem"</span>
      productsCountElement.style.fontSize = <span class="hljs-string">"2em"</span>
    }
  }
  const [cart,] = useLocalStorageState&lt;CartProps&gt;(<span class="hljs-string">'cart'</span>, {})

  const productsCount: number = Object.keys(cart || {}).length

  <span class="hljs-keyword">return</span> (
    &lt;header className={classes.header}&gt;
      &lt;div&gt;
        &lt;Link to=<span class="hljs-string">"/"</span>&gt;
          &lt;img src={logo} className={classes.logo} alt=<span class="hljs-string">"Shopping Cart Application"</span> /&gt;
        &lt;/Link&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;CartWidget productsCount={productsCount} /&gt;
      &lt;/div&gt;
    &lt;/header&gt;
  )
}
</code></pre>
<h3 id="heading-how-to-build-the-product-list">🧩 How to Build the Product List</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-22-at-11.03.49.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Product List</em></p>
<p>To construct the product list component, I followed the same approach as above. Initially, I established the foundational structure, which involved creating the component code, exporting the component in the index file, and implementing a separate SCSS file to define the component's styles.</p>
<p>As a result, the folder structure after completing these steps should resemble the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-22-at-11.06.23.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Product List Component Folder</em></p>
<p>One interesting aspect of this component is that it handles sending a request to a REST API in order to fetch the product data. This is accomplished through the <code>fetchData</code> method, which is invoked within a <code>useEffect</code> hook.</p>
<p>By specifying an empty dependency array, the code inside the <code>useEffect</code> hook is executed only once when the component is initially loaded. This ensures that redundant requests are avoided, optimizing the performance of our application and reducing bandwidth usage.</p>
<pre><code class="lang-typescript"> useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchData(API_URL)
  }, [])


  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchData</span>(<span class="hljs-params">url: <span class="hljs-built_in">string</span></span>) </span>{
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(url)
      <span class="hljs-keyword">if</span> (response.ok) {
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json()
        setProducts(data.products)
        setIsLoading(<span class="hljs-literal">false</span>)
      } <span class="hljs-keyword">else</span> {
        setError(<span class="hljs-literal">true</span>)
        setIsLoading(<span class="hljs-literal">false</span>)
      }
    } <span class="hljs-keyword">catch</span> (error) {
      setError(<span class="hljs-literal">true</span>)
      setIsLoading(<span class="hljs-literal">false</span>)
    }
  }
</code></pre>
<p>The rendering aspect of the component is relatively straightforward. Once we have successfully fetched the product data, we can iterate through it using a regular <code>map()</code> function. For each product, we can render its thumbnail image, title, price, and a button for adding it to the cart.</p>
<p>To ensure that each row displays three items when viewed on large viewports, we utilize CSS (SCSS) styles. We'll do this by harnessing the capabilities of <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox">Flexbox</a>, as demonstrated in the following snippet:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.productPage</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">8rem</span>;

  <span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-wrap</span>: wrap;
    <span class="hljs-attribute">justify-content</span>: space-between;

    <span class="hljs-selector-class">.product</span> {
      <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">33.33%</span>;
      <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">5rem</span>;
      <span class="hljs-attribute">text-align</span>: center;

      <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#007185</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">20px</span>;
        <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
      }

      <span class="hljs-selector-tag">img</span> {
        <span class="hljs-attribute">height</span>: <span class="hljs-number">6rem</span>;
      }

      <span class="hljs-selector-tag">button</span> {
        <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fbd815</span>;
        <span class="hljs-attribute">width</span>: <span class="hljs-number">13rem</span>;
        <span class="hljs-attribute">padding</span>: .<span class="hljs-number">5rem</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.1em</span>;
        <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">25px</span>;
        <span class="hljs-attribute">border-color</span>: <span class="hljs-number">#D5D9D9</span>;
        <span class="hljs-attribute">border-style</span>: solid;
        <span class="hljs-attribute">border-width</span>: <span class="hljs-number">1px</span>;

        &amp;<span class="hljs-selector-pseudo">:hover</span><span class="hljs-selector-pseudo">:not</span>(<span class="hljs-selector-attr">[disabled]</span>) {
          <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#eecf1d</span>;
          <span class="hljs-attribute">cursor</span>: pointer;
        }

        &amp;<span class="hljs-selector-pseudo">:disabled</span> {
          <span class="hljs-attribute">opacity</span>: .<span class="hljs-number">5</span>;
          <span class="hljs-attribute">background-color</span>: lightgray;
        }
      }
    }

    <span class="hljs-keyword">@media</span> (max-width: <span class="hljs-number">767px</span>) {
      <span class="hljs-selector-class">.product</span> {
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">50%</span>;
      }
    }

    <span class="hljs-keyword">@media</span> (max-width: <span class="hljs-number">400px</span>) {
      <span class="hljs-selector-class">.product</span> {
        <span class="hljs-attribute">flex-basis</span>: <span class="hljs-number">100%</span>;
      }
    }
  }
}

<span class="hljs-selector-class">.error</span> {
  <span class="hljs-attribute">color</span>: red;
  <span class="hljs-attribute">text-align</span>: center;
}
</code></pre>
<p>In addition to these features, the component code also includes functionality for handling the "Add to Cart" button click event, specifically adding the selected item to the local storage. We also implement basic error handling logic to display an error message in case the request to the third-party API fails.</p>
<p>Here is the completed component:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FunctionComponent, useEffect, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> useLocalStorageState <span class="hljs-keyword">from</span> <span class="hljs-string">'use-local-storage-state'</span>

<span class="hljs-keyword">import</span> { CurrencyFormatter } <span class="hljs-keyword">from</span> <span class="hljs-string">'../CurrencyFormatter'</span>
<span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">'./products.module.scss'</span>
<span class="hljs-keyword">import</span> { Loader } <span class="hljs-keyword">from</span> <span class="hljs-string">'../Loader'</span>

<span class="hljs-keyword">const</span> API_URL = <span class="hljs-string">'https://dummyjson.com/products'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> Product = {
  id: <span class="hljs-built_in">number</span>
  title: <span class="hljs-built_in">string</span>
  price: <span class="hljs-built_in">number</span>
  thumbnail: <span class="hljs-built_in">string</span>
  image: <span class="hljs-built_in">string</span>
  quantity: <span class="hljs-built_in">number</span>
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CartProps {
  [productId: <span class="hljs-built_in">string</span>]: Product
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Products: FunctionComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">true</span>)
  <span class="hljs-keyword">const</span> [products, setProducts] = useState&lt;Product[]&gt;([])
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">false</span>)
  <span class="hljs-keyword">const</span> [cart, setCart] = useLocalStorageState&lt;CartProps&gt;(<span class="hljs-string">'cart'</span>, {})


  useEffect(<span class="hljs-function">() =&gt;</span> {
    fetchData(API_URL)
  }, [])


  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchData</span>(<span class="hljs-params">url: <span class="hljs-built_in">string</span></span>) </span>{
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(url)
      <span class="hljs-keyword">if</span> (response.ok) {
        <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> response.json()
        setProducts(data.products)
        setIsLoading(<span class="hljs-literal">false</span>)
      } <span class="hljs-keyword">else</span> {
        setError(<span class="hljs-literal">true</span>)
        setIsLoading(<span class="hljs-literal">false</span>)
      }
    } <span class="hljs-keyword">catch</span> (error) {
      setError(<span class="hljs-literal">true</span>)
      setIsLoading(<span class="hljs-literal">false</span>)
    }
  }

  <span class="hljs-keyword">const</span> addToCart = (product: Product):<span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
    product.quantity = <span class="hljs-number">1</span>

    setCart(<span class="hljs-function">(<span class="hljs-params">prevCart</span>) =&gt;</span> ({
      ...prevCart,
      [product.id]: product,
    }))
  }

  <span class="hljs-keyword">const</span> isInCart = (productId: <span class="hljs-built_in">number</span>):<span class="hljs-function"><span class="hljs-params">boolean</span> =&gt;</span> <span class="hljs-built_in">Object</span>.keys(cart || {}).includes(productId.toString())

  <span class="hljs-keyword">if</span> (error) {
    <span class="hljs-keyword">return</span> &lt;h3 className={classes.error}&gt;An error occurred when fetching data. Please check the API and <span class="hljs-keyword">try</span> again.&lt;/h3&gt;
  }

  <span class="hljs-keyword">if</span> (isLoading) {
    <span class="hljs-keyword">return</span> &lt;Loader /&gt;
  }


  <span class="hljs-keyword">return</span> (
    &lt;section className={classes.productPage}&gt;
      &lt;h1&gt;Products&lt;/h1&gt;

      &lt;div className={classes.container}&gt;
        {products.map(<span class="hljs-function"><span class="hljs-params">product</span> =&gt;</span> (
          &lt;div className={classes.product} key={product.id}&gt;
            &lt;img src={product.thumbnail} alt={product.title} /&gt;
            &lt;h3&gt;{product.title}&lt;/h3&gt;
            &lt;p&gt;Price: &lt;CurrencyFormatter amount={product.price} /&gt;&lt;/p&gt;
            &lt;button disabled={isInCart(product.id)} onClick={<span class="hljs-function">() =&gt;</span> addToCart(product)}&gt;Add to Cart&lt;/button&gt;
          &lt;/div&gt;
        ))}
      &lt;/div&gt;
    &lt;/section&gt;
  )
}
</code></pre>
<h3 id="heading-how-to-build-the-cart">🧩 How to Build the Cart</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-22-at-11.31.02.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cart Component</em></p>
<p>This component bears some resemblance to the Product List component in that it also lists products, but in a different manner with only one item per row.</p>
<p>It also introduces additional functionality by incorporating another component for updating the quantity of selected products. And it calculates the total price of all products in the cart.</p>
<pre><code class="lang-typescript"> &lt;section className={classes.cart}&gt;
      &lt;h1&gt;Cart&lt;/h1&gt;

      &lt;div className={classes.container}&gt;
        {getProducts().map(<span class="hljs-function"><span class="hljs-params">product</span> =&gt;</span> (
          &lt;div className={classes.product} key={product.id}&gt;
            &lt;img src={product.thumbnail} alt={product.title} /&gt;
            &lt;h3&gt;{product.title}&lt;/h3&gt;
            &lt;Quantifier
              removeProductCallback={<span class="hljs-function">() =&gt;</span> handleRemoveProduct(product.id)}
              productId={product.id}
              handleUpdateQuantity={handleUpdateQuantity} /&gt;
          &lt;/div&gt;
        ))}
      &lt;/div&gt;
      &lt;TotalPrice amount={totalPrice} /&gt;
    &lt;/section&gt;
</code></pre>
<p>The main distinction here is that instead of fetching the product data from an API, we retrieve it from the local storage. This is where we store the data for each selected product from the product list component.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> [cart, setCart] = useLocalStorageState&lt;CartProps&gt;(<span class="hljs-string">'cart'</span>, {}) <span class="hljs-comment">// reading the local storage value via the hook here</span>

....
....

  <span class="hljs-keyword">const</span> getProducts = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Object</span>.values(cart || {}) <span class="hljs-comment">// method for getting all products data as an array data structure, that will allow us easier iteration later</span>

....
....
</code></pre>
<p>In this case, we use the <code>useEffect</code> hook once again, but this time to reset the scroll position of the window whenever the user visits the page. This ensures that all relevant data is consistently visible to the user, regardless of how far they have scrolled on the product list page.</p>
<pre><code class="lang-typescript">
  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.scrollTo(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
  }, [location])
</code></pre>
<p>Indeed, you can see that the methods for decreasing or increasing the quantity of a product are passed to the component as callbacks through its props. This approach is useful as it helps maintain a relatively clean component by elevating the responsibility of state management to a higher level.</p>
<p>By lifting the logic for managing the state outside of the component, it allows for better separation of concerns and promotes reusability.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> handleRemoveProduct = (productId: <span class="hljs-built_in">number</span>): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
    setCart(<span class="hljs-function">(<span class="hljs-params">prevCart</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> updatedCart = { ...prevCart }
      <span class="hljs-keyword">delete</span> updatedCart[productId]
      <span class="hljs-keyword">return</span> updatedCart
    })
  }

  <span class="hljs-keyword">const</span> handleUpdateQuantity = <span class="hljs-function">(<span class="hljs-params">productId: <span class="hljs-built_in">number</span>, operation: Operation</span>) =&gt;</span> {
    setCart(<span class="hljs-function">(<span class="hljs-params">prevCart</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> updatedCart = { ...prevCart }
      <span class="hljs-keyword">if</span> (updatedCart[productId]) {
        <span class="hljs-keyword">if</span> (operation === <span class="hljs-string">'increase'</span>) {
          updatedCart[productId] = { ...updatedCart[productId], quantity: updatedCart[productId].quantity + <span class="hljs-number">1</span> }
        } <span class="hljs-keyword">else</span> {
          updatedCart[productId] = { ...updatedCart[productId], quantity: updatedCart[productId].quantity - <span class="hljs-number">1</span> }
        }
      }
      <span class="hljs-keyword">return</span> updatedCart
    })
  }
</code></pre>
<p>We can style the component using Flexbox to get our desired layout:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.cart</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">margin-top</span>: <span class="hljs-number">8rem</span>;

  <span class="hljs-selector-class">.container</span> {
    <span class="hljs-attribute">display</span>: flex;
    <span class="hljs-attribute">flex-direction</span>: column;

    <span class="hljs-selector-class">.product</span> {
      <span class="hljs-attribute">display</span>: flex;
      <span class="hljs-attribute">border-top</span>: <span class="hljs-number">1px</span> dotted;
      <span class="hljs-attribute">border-left</span>: <span class="hljs-number">1px</span> dotted;
      <span class="hljs-attribute">border-right</span>: <span class="hljs-number">1px</span> dotted;
      <span class="hljs-attribute">padding</span>: .<span class="hljs-number">3rem</span> .<span class="hljs-number">5rem</span>;
      <span class="hljs-attribute">align-items</span>: center;

      <span class="hljs-selector-tag">h3</span> {
        <span class="hljs-attribute">color</span>: <span class="hljs-number">#007185</span>;
        <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
        <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1em</span>;
        <span class="hljs-attribute">line-height</span>: <span class="hljs-number">20px</span>;
        <span class="hljs-attribute">margin</span>: .<span class="hljs-number">3rem</span>;
        <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
      }

      <span class="hljs-selector-tag">img</span> {
        <span class="hljs-attribute">max-width</span>: <span class="hljs-number">3rem</span>;
        <span class="hljs-attribute">height</span>: auto;
        <span class="hljs-attribute">margin</span>: .<span class="hljs-number">875rem</span>;
      }
    }
  }
}
</code></pre>
<p>Here is the final version of the component's code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FunctionComponent, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> useLocalStorageState <span class="hljs-keyword">from</span> <span class="hljs-string">'use-local-storage-state'</span>

<span class="hljs-keyword">import</span> { Quantifier } <span class="hljs-keyword">from</span> <span class="hljs-string">'../Quantifier'</span>
<span class="hljs-keyword">import</span> { CartProps } <span class="hljs-keyword">from</span> <span class="hljs-string">'../Products/Products.tsx'</span>
<span class="hljs-keyword">import</span> { TotalPrice } <span class="hljs-keyword">from</span> <span class="hljs-string">'../TotalPrice'</span>
<span class="hljs-keyword">import</span> { Operation } <span class="hljs-keyword">from</span> <span class="hljs-string">'../Quantifier/Quantifier.tsx'</span>
<span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">'./cart.module.scss'</span>
<span class="hljs-keyword">import</span> { useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router-dom'</span>


<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Cart: FunctionComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [cart, setCart] = useLocalStorageState&lt;CartProps&gt;(<span class="hljs-string">'cart'</span>, {})
  <span class="hljs-keyword">const</span> location = useLocation()

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">window</span>.scrollTo(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
  }, [location])

  <span class="hljs-keyword">const</span> handleRemoveProduct = (productId: <span class="hljs-built_in">number</span>): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
    setCart(<span class="hljs-function">(<span class="hljs-params">prevCart</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> updatedCart = { ...prevCart }
      <span class="hljs-keyword">delete</span> updatedCart[productId]
      <span class="hljs-keyword">return</span> updatedCart
    })
  }

  <span class="hljs-keyword">const</span> handleUpdateQuantity = <span class="hljs-function">(<span class="hljs-params">productId: <span class="hljs-built_in">number</span>, operation: Operation</span>) =&gt;</span> {
    setCart(<span class="hljs-function">(<span class="hljs-params">prevCart</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> updatedCart = { ...prevCart }
      <span class="hljs-keyword">if</span> (updatedCart[productId]) {
        <span class="hljs-keyword">if</span> (operation === <span class="hljs-string">'increase'</span>) {
          updatedCart[productId] = { ...updatedCart[productId], quantity: updatedCart[productId].quantity + <span class="hljs-number">1</span> }
        } <span class="hljs-keyword">else</span> {
          updatedCart[productId] = { ...updatedCart[productId], quantity: updatedCart[productId].quantity - <span class="hljs-number">1</span> }
        }
      }
      <span class="hljs-keyword">return</span> updatedCart
    })
  }


  <span class="hljs-keyword">const</span> getProducts = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">Object</span>.values(cart || {})

  <span class="hljs-keyword">const</span> totalPrice = getProducts().reduce(<span class="hljs-function">(<span class="hljs-params">accumulator, product</span>) =&gt;</span> accumulator + (product.price * product.quantity), <span class="hljs-number">0</span>)

  <span class="hljs-keyword">return</span> (
    &lt;section className={classes.cart}&gt;
      &lt;h1&gt;Cart&lt;/h1&gt;

      &lt;div className={classes.container}&gt;
        {getProducts().map(<span class="hljs-function"><span class="hljs-params">product</span> =&gt;</span> (
          &lt;div className={classes.product} key={product.id}&gt;
            &lt;img src={product.thumbnail} alt={product.title} /&gt;
            &lt;h3&gt;{product.title}&lt;/h3&gt;
            &lt;Quantifier
              removeProductCallback={<span class="hljs-function">() =&gt;</span> handleRemoveProduct(product.id)}
              productId={product.id}
              handleUpdateQuantity={handleUpdateQuantity} /&gt;
          &lt;/div&gt;
        ))}
      &lt;/div&gt;
      &lt;TotalPrice amount={totalPrice} /&gt;
    &lt;/section&gt;
  )
}
</code></pre>
<h3 id="heading-how-to-build-the-footer">🧩 How to Build the Footer</h3>
<p>To enhance the overall appearance of the application, I have included a footer component. Here is an example of how it looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/footer.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The implementation of the footer component is relatively straightforward. It consists of two links to social platforms and a copyright text.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { FunctionComponent } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">"./footer.module.scss"</span>
<span class="hljs-keyword">import</span> packageJson <span class="hljs-keyword">from</span> <span class="hljs-string">'../../../package.json'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Footer: FunctionComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> currentYear = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear()

  <span class="hljs-keyword">return</span> (
    &lt;footer className={classes.footer} data-cy=<span class="hljs-string">"footer"</span>&gt;
      &lt;ul&gt;
        &lt;li className={classes.footerLinks}&gt;
          &lt;a
            href=<span class="hljs-string">"https://twitter.com/mihailgaberov"</span>
            target=<span class="hljs-string">"_blank"</span>
            rel=<span class="hljs-string">"noopener noreferrer"</span>
            data-cy=<span class="hljs-string">"twitterLink"</span>
          &gt;
            twitter
          &lt;/a&gt;{<span class="hljs-string">" "</span>}
          &amp;bull;{<span class="hljs-string">" "</span>}
          &lt;a
            href=<span class="hljs-string">"https://github.com/mihailgaberov"</span>
            target=<span class="hljs-string">"_blank"</span>
            rel=<span class="hljs-string">"noopener noreferrer"</span>
            data-cy=<span class="hljs-string">"githubLink"</span>
          &gt;
            github
          &lt;/a&gt;
        &lt;/li&gt;
        &lt;li className={classes.footerCopyrights}&gt;
          © {packageJson.author} {currentYear}. All rights reserved.
        &lt;/li&gt;
        &lt;li&gt;
          &lt;div className={classes.version}&gt;v.{packageJson.version}&lt;/div&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
    &lt;/footer&gt;
  )
}
</code></pre>
<p>Again, in the <a target="_blank" href="https://github.com/mihailgaberov/shopping-cart-app/tree/main/src/components/Footer">component folder</a> you will find the other files that are necessary for applying the styles and the tests for that component.</p>
<h2 id="heading-testing-the-app">🧪 Testing the App</h2>
<p>I am sure you've already noticed that in all components folders, except the Loader one, we have .test.tsx files. These are the files containing the component tests that we run on the front end.</p>
<p>In order to do a test run, open your CLI and run the following command from the root directory of your project:</p>
<pre><code class="lang-bash">yarn <span class="hljs-built_in">test</span>
</code></pre>
<p>This should run all tests and give you the following output:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-22-at-12.05.23.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Application Tests</em></p>
<p>Now that we've built all the necessary components, let's delve into a more detailed discussion about local storage and how we can leverage it to manage the application state more effectively.</p>
<h2 id="heading-what-is-localstorage">🧠 What is LocalStorage?</h2>
<p>Before we dive into the details, let's begin with a brief explanation of what local storage is. This will ensure that everyone, including those who are unfamiliar with it, can easily understand how we use this in the app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-226.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>App LocalStorage</em></p>
<p>LocalStorage is a feature provided by web browsers that allows web applications to store data locally on the user's device. It provides a simple key-value storage mechanism, similar to a dictionary or associative array.</p>
<p>Unlike session storage, which is temporary and gets cleared when the browser session ends, local storage persists even after the browser is closed and reopened. The data stored in local storage remains available until explicitly removed by the application or cleared by the user.</p>
<p>Local storage is primarily used for client-side data storage, enabling web applications to save user preferences, session data, or any other relevant information. This makes it a useful tool for creating personalized experiences and maintaining state across multiple visits to a website.</p>
<p>With that being said, you may have already guessed that local storage can be utilized to store the data of items added to the cart by the user. By doing so, when we navigate to the cart page, our application will be aware of which items should be displayed there.</p>
<p>To accomplish this, I utilized a React hook called <a target="_blank" href="https://www.npmjs.com/package/use-local-storage-state">use-local-storage-state</a>.</p>
<p>In the Product List component, we added products to the LocalStorage whenever the user clicked on the <code>Add to Cart</code> button for each item. This ensured that the selected products were stored persistently.</p>
<p>Then, when the user navigates to the cart page, we retrieve the stored data from the LocalStorage and render the products accordingly. By doing so, the user can easily view and interact with the products they previously added to their cart.</p>
<p>Here is a snippet from the code itself.</p>
<p>We add the items to the LocalStorage like this:</p>
<pre><code class="lang-python">const [cart, setCart] = useLocalStorageState&lt;CartProps&gt;(<span class="hljs-string">'cart'</span>, {})
...
...
...

const addToCart = (product: Product):void =&gt; {
    product.quantity = <span class="hljs-number">1</span>

    setCart((prevCart) =&gt; ({
      ...prevCart,
      [product.id]: product,
    }))
  }
</code></pre>
<p>And then read the data like this:</p>
<pre><code class="lang-python">const [cart, setCart] = useLocalStorageState&lt;CartProps&gt;(<span class="hljs-string">'cart'</span>, {})
...
...
...

const getProducts = () =&gt; Object.values(cart || {})
</code></pre>
<p>And then render them like this:</p>
<pre><code class="lang-jsx">&lt;div className={classes.container}&gt;
        {getProducts().map(<span class="hljs-function"><span class="hljs-params">product</span> =&gt;</span> (
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.product}</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{product.id}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{product.thumbnail}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{product.title}</span> /&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h3</span>&gt;</span>{product.title}<span class="hljs-tag">&lt;/<span class="hljs-name">h3</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Quantifier</span>
              <span class="hljs-attr">removeProductCallback</span>=<span class="hljs-string">{()</span> =&gt;</span> handleRemoveProduct(product.id)}
              productId={product.id}
              handleUpdateQuantity={handleUpdateQuantity} /&gt;
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        ))}
      &lt;/div&gt;
</code></pre>
<h2 id="heading-how-to-automate-unit-testing-with-github-actions">How to Automate Unit Testing with GitHub Actions</h2>
<p>GitHub Actions is a powerful automation tool provided by GitHub. It allows you to define and execute workflows directly within your GitHub repository.</p>
<p>With GitHub Actions, you can automate various tasks and processes, such as building, testing, and deploying your code, as well as performing code analysis, generating documentation, and more.</p>
<p>GitHub Actions workflows are defined using YAML syntax. A workflow consists of one or more jobs, and each job consists of a series of steps to be executed. Steps can include actions (reusable units of code), shell commands, or scripts.</p>
<pre><code class="lang-python">name: Shopping Cart CI

on:
  push:
    branches: [ <span class="hljs-string">"main"</span> ]
  pull_request:
    branches: [ <span class="hljs-string">"main"</span> ]

jobs:
  build:

    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [<span class="hljs-number">19.</span>x]
        <span class="hljs-comment"># See supported Node.js release schedule at https://nodejs.org/en/about/releases/</span>

    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        <span class="hljs-keyword">with</span>:
          node-version: <span class="hljs-string">'19'</span>
          cache: <span class="hljs-string">'yarn'</span>
      - run: yarn install
      - run: yarn test
</code></pre>
<p>In our case, I utilized GitHub Actions to create a workflow that automatically runs the unit test suite whenever a new change is committed to the main branch. This ensures that no production build is triggered in the event of a test failure.</p>
<p>Implementing this workflow is straightforward and highly beneficial. It provides the reassurance that any issues inadvertently introduced into the main branch will be caught before reaching production. This level of reliability and peace of mind is easily achieved by leveraging the capabilities of GitHub Actions.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-227.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Shopping Cart app - GitHub Actions</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The topic covered in this tutorial may not be novel or unfamiliar, but its scope of application is remarkably extensive.</p>
<p>Nearly every online application that involves trading or purchasing goods incorporates a shopping cart or basket functionality in some form. Because of this, if you're a software engineer working in this domain, you'll need to possess a solid understanding of how to implement this functionality and its underlying principles.</p>
<p>By acquiring a high-level knowledge of these concepts and understanding how things operate in general, we can effectively contribute to the development of robust and efficient e-commerce systems.</p>
<p>📘 You now understand the purpose and functionality of a shopping cart in an online application.</p>
<p>📘 You have learned how to implement a product list page by fetching data from a third-party REST API.</p>
<p>📘 You have learned how to create a cart component that retrieves and renders data from the browser's local storage.</p>
<p>📘 You have discovered the benefits of GitHub Actions and how they can provide confidence when promoting your code to production.</p>
<p>📘 And most importantly, you have embraced the mindset of continuous learning, understanding that there is always more to explore and discover in the vast world of software engineering.</p>
<p>By combining these newfound skills with a commitment to ongoing learning, you are well-equipped to create efficient and robust e-commerce systems while continuously improving your abilities in this dynamic field. Remember, the journey of knowledge acquisition never ends! 🎓</p>
<p>I hope you found this tutorial enjoyable and engaging. Most importantly, I hope you were able to actively follow along and even experiment with the code. Feel free to make your own modifications and observe the resulting changes in the end product. Hands-on exploration and experimentation are fantastic ways to solidify your understanding and expand your skills.</p>
<p>Remember, the best way to enhance your skills and understanding is through practice and experimentation. So feel free to dive into the code, make it your own, and observe the exciting results you can achieve. Happy coding and exploring!</p>
<p>Thanks for reading! 🙏</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Pub/Sub in Redis – How to Use the Publish/Subscribe Messaging Pattern ]]>
                </title>
                <description>
                    <![CDATA[ When you're working on an application that needs to be easily maintainable, scalable, and performant, the Publish/Subscribe messaging pattern is a good choice. The idea behind it is simple, but powerful. We have senders called publishers. Their sole ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-pub-sub-in-redis/</link>
                <guid isPermaLink="false">66d460569f2bec37e2da0654</guid>
                
                    <category>
                        <![CDATA[ messaging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Fri, 28 Apr 2023 14:33:25 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/image-332-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>When you're working on an application that needs to be easily maintainable, scalable, and performant, the Publish/Subscribe messaging pattern is a good choice.</p>
<p>The idea behind it is simple, but powerful. We have senders called <em>publishers.</em> Their sole role is to send or <em>publish</em> messages. They don’t care about who is going to receive them or if someone will receive them at all. They just shoot and forget the messages. And they do that via <em>channels</em>.</p>
<p>Think of them as, for example, TV channels. We have Sports channels, Weather Forecasting channels, Cooking channels, and so on. Every publisher sends its messages to a certain channel, and whoever is <em>subscribed</em> for this channel will be able to receive these messages.</p>
<p>Here is where the <em>subscribers</em> come in play. They can subscribe to one or more channels and start receiving the messages broadcasted in there.</p>
<p>As we already mentioned, the messages are to be sent and forgotten. This means that if a subscriber subscribes for a certain channel, all the messages that were sent previously in that channel are not going to be available to this subscriber.</p>
<p>Due to the nature of this kind of architecture, we can easily achieve low coupling between the different components and provide a solid foundation for building robust and easy-to-maintain applications.</p>
<p>For example, imagine a situation where we need to replace or improve the publishing part of our system – say add more publishers, more channels or so on. Since the two parts are isolated, meaning publishers don’t care about subscribers and vice versa, we could easily do that without worrying whether we are breaking some other part of the system. We just add the new publishers. Then later, when a subscriber comes to the relevant channels, it just starts using them.</p>
<h2 id="heading-what-is-redis">What is Redis?</h2>
<p>The initial idea behind Redis was to serve as an in-memory cache solution, as an alternative to its ancestor <a target="_blank" href="https://www.memcached.org/">Memcached</a>.</p>
<p>But nowadays it's a many-in-one solution, providing an in-memory data structure store, key-value database, message brokering, and so on. This makes it perfect candidate when building an application that needs a really fast caching solution as well as some of the other features mentioned before. Especially if the performance of the app is crucial for its regular usage.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/image-333.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Redis performance comparison (source: google)</em></p>
<p>One of the biggest advantages when using Redis is the huge community and technical resources you can find online. A lot of these resources are free, and there are online platforms that have free tier offerings.</p>
<p>Redis includes in its arsenal a cloud solution as well. If you want to try it yourself, you may go <a target="_blank" href="https://redis.com/try-free/">here</a> and register a free account or use their initial coupon offering.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/image-334.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Redis Enterprise Cloud Sign Up / Sign In page</em></p>
<h2 id="heading-pubsub-in-redis">Pub/Sub in Redis</h2>
<h3 id="heading-what-is-pubsub">What is pub/sub?</h3>
<p><a target="_blank" href="https://redis.io/docs/manual/pubsub/">Publish/Subscribe channels in Redis</a> is one of the features I haven’t mentioned above but it’s included in the last versions of Redis. This is their implementation of the <a target="_blank" href="https://redis.io/docs/manual/pubsub/">pub/sub messaging pattern</a>, where we have publishers and subscribers that exchange messages via channels.</p>
<p>We'll go briefly through it below and then see it in practice in a <a target="_blank" href="https://github.com/mihailgaberov/redis-pub-sub-visualized">small demo app</a> I have prepared for you.</p>
<h3 id="heading-how-does-redis-pubsub-work">How does Redis pub/sub work?</h3>
<p>We have publishers (the producers of messages), channels (that the messages are going through), and subscribers (the receivers of the messages). Who receives what depends solely on who is subscribed to which channel.</p>
<p><strong>Let's see how this works in an example:</strong></p>
<p>If we have created three publishers which will be publishing messages to three different channels. Let’s call them channels 1, 2 and 3. We also have three subscribers, let’s call them subscribers A, B and C.</p>
<p>Now, let’s imagine subscriber A is listening for messages on all three channels, that is, it's subscribed to them. And subscribers B and C are subscribed to channels 2 and 3. This means that when either of the three publishers sends a message, subscriber A will receive it. And subscribers B and C will be receiving messages sent only by publishers 2 and 3, because they are listening only for messages on these channels (2 and 3).</p>
<p>Notice that we have two entities using a channel – one is sending, the other is receiving – but they are totally independent. And the messages being sent are not persisted. Once they are sent by the publisher they are forgotten. The only entities that are subscribed at the moment of sending will get them.</p>
<h3 id="heading-how-to-use-pubsub-in-redis">How to use pub/sub in Redis</h3>
<p>There are a plethora of client libraries that you can use with Redis. There is a <a target="_blank" href="https://redis.io/docs/clients/">dedicated page</a> where everybody can go and pick one, depending on the specific project needs or just on your preferred programming language.</p>
<p>People at Redis also marked some of these repositories as <em>recommended</em> which makes the choice easier, if you are new to all this.</p>
<p>For our demo below, I used <a target="_blank" href="https://github.com/luin/ioredis">ioredis</a>, a full-featured Redis client for Node.js. I chose this because the demo app UI is built with React and Node.js and my server code goes pretty well with it.</p>
<h2 id="heading-redis-pubsub-demo">Redis Pub/Sub Demo</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/image-336.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Redis Pub/Sub Visualizer app</em></p>
<p>Show time!</p>
<p>The idea behind the <a target="_blank" href="https://github.com/mihailgaberov/redis-pub-sub-visualized">demo application</a> is to show visually how the pattern works.</p>
<p>What you will see when you open it for first the time is three buttons for publishing simple messages (news) in the three imaginary TV channels: Weather, Sports and Music.</p>
<p>The cards below the publish buttons are the subscribers. Once you move your mouse cursor over any of them, it will flip to its back side and you will see three buttons. You may use each of these buttons to subscribe to the relevant channel.</p>
<p>Once a subscriber is signed over a channel and you click on the icon or the publish button for this channel, you will see a sample news appearing on the front side of the card.</p>
<p>Play with different publishers/subscribers combinations and see the result.</p>
<p>I hope this will give you a better understanding of what I explained in the example above.</p>
<h3 id="heading-how-to-run-the-demo-app-locally"><strong>How to run the demo app locally</strong></h3>
<p>In order to install and run the demo application locally, follow the steps below (all commands are considered to be run from the root project directory):</p>
<p><strong>Run frontend:</strong></p>
<p><code>cd client yarn &amp;&amp; yarn dev</code></p>
<p><strong>Run backend:</strong></p>
<p><code>cd server &amp;&amp; yarn yarn start</code></p>
<p>And finally, use your local installation of Docker (if don’t have one, you may get it from <a target="_blank" href="https://docs.docker.com/get-docker/">here</a>) to run this:</p>
<pre><code class="lang-python">docker run -p <span class="hljs-number">6379</span>:<span class="hljs-number">6379</span> redislabs/redismod:preview
</code></pre>
<p>That’s probably the easiest way to have a running copy of Redis locally. The other option would be to use <a target="_blank" href="https://redis.com/try-free/">Redis Cloud</a> directly and deploy the application online. This is an option I am still investigating and if I manage to do it, I will deploy the whole app and will let you know.</p>
<h2 id="heading-closing">Closing</h2>
<p>This article introduced you to the pub/sub messaging pattern subject. It's important to remember that whenever we want to build a highly performant application with a low coupled architecture and real-time like messaging features, consider using the Publish/Subscribe pattern and Redis in particular.</p>
<p>In fact a lot of the real life applications that use Redis are dashboard-based. This means that usually there is a nice dashboard screen, showing different data, often being updated in real time.</p>
<p>Imagine, for example, a system showing traffic in a specific area. This kind of software is a perfect candidate for leveraging the advantages of pub/sub. And in many cases this is achieved by using Redis.</p>
<p>In any case, as developers and engineers, we should always be guided by the specific needs of the project we are working on. Whenever we decide to introduce a new pattern or technology, we should do it carefully and back it up with serious research.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Live Football Scoreboard in React with Vite and Vitest ]]>
                </title>
                <description>
                    <![CDATA[ Welcome to yet another tutorial where you will learn how to build a ⚽ scoreboard app in React. This time we will use Vite as our next generation frontend tooling and Vitest as a Vite-native unit testing framework. You will also learn how to leverage ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-project-with-vite-and-vitest/</link>
                <guid isPermaLink="false">66d4605cb3016bf139028d71</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ vite ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Thu, 13 Apr 2023 20:41:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-13-at-5.15.35-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Welcome to yet another <a target="_blank" href="https://www.mihailgaberov.com/">tutorial</a> where you will learn how to build a ⚽ scoreboard app in React. This time we will use Vite as our <a target="_blank" href="https://vitejs.dev/">next generation frontend tooling</a> and <a target="_blank" href="https://vitest.dev/">Vitest</a> as a Vite-native unit testing framework.</p>
<p>You will also learn how to leverage <a target="_blank" href="https://react.dev/learn/reusing-logic-with-custom-hooks">React hooks</a>, built as an abstraction of the native JavaScript timeouts and time intervals. For styling the application we will use <a target="_blank" href="https://github.com/css-modules/css-modules">CSS Modules</a> with <a target="_blank" href="https://sass-lang.com/">SASS</a>.</p>
<p>Here's what we'll be building:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-04-at-09.11.31.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Football live scoreboard - current games screen</em></p>
<p>💡If you want to skip the reading, 💁 <a target="_blank" href="https://github.com/mihailgaberov/scoreboard">here</a> is the GitHub repository, and here you can see the live <a target="_blank" href="https://scoreboard-mihailgaberov.vercel.app/">demo</a> 📺.</p>
<h2 id="heading-what-is-a-scoreboard">What is a Scoreboard?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-04-at-11.58.35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Football scoreboard</em></p>
<p>A live scoreboard is a digital sport scoreboard that automatically displays up-to-the-minute sports scores and data from a certain game – for example a football game. This way it’s much easier for the users to follow the game, make predictions or bets, and so on.</p>
<p>Our application is going to reflect such a board, but in the browser.</p>
<h2 id="heading-the-project">The Project</h2>
<p>Our application has just a few dependencies and several components. It also uses JavaScript timeouts and intervals to simulate real-time score updates.</p>
<h3 id="heading-application-features"><strong>⚙️ Application features</strong></h3>
<p>Before going into the technical part of the tutorial, let's talk about the application features we will implement.</p>
<p>It’s always better (if possible, of course) to have clear project requirements laid out before writing a single line of code. But folks with some experience in the software engineering and development world know that the reality is often completely different.</p>
<p>The beauty of such small projects that you build for educational purposes is exactly this – you have the freedom to define your own requirements and to meet them in a feasible manner.</p>
<p>So here it the summary of the requirements/features:</p>
<p><strong>Live Football World Cup Scoreboard</strong> that shows matches and scores.</p>
<p>The board supports the following operations:</p>
<ol>
<li><p>Start a game. When a game starts, it should capture the home team and away team (with an initial score of 0 – 0).</p>
</li>
<li><p>Finish game. It will remove a match from the scoreboard.</p>
</li>
<li><p>Update score. Receiving the pair's score. When the home team or away team scores, it updates the game score.</p>
</li>
<li><p>Get a summary of games by total score. Those games with the same total score will be returned ordered by the most recently added to our system.</p>
</li>
</ol>
<p>✍️ As an example, if this is the current data in the system:</p>
<p><code>a. Mexico - Canada: 0 - 5</code><br><code>b. Spain - Brazil: 10 – 2</code><br><code>c. Germany - France: 2 – 2</code><br><code>d. Uruguay - Italy: 6 – 6</code><br><code>e. Argentina - Australia: 3 - 1</code></p>
<p>The summary would give us the following information:</p>
<p><code>1. Uruguay 6 - Italy 6</code><br><code>2. Spain 10 - Brazil 2</code><br><code>3. Mexico 0 - Canada 5</code><br><code>4. Argentina 3 - Australia 1</code><br><code>5. Germany 2 - France 2</code></p>
<h3 id="heading-project-structure">🏗️ Project Structure</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/Screenshot-2023-04-04-at-15.08.38.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Project Structure</em></p>
<p>Let me go through each of the files and give a short explanation of what they are and why we need them:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/package.json">package.json</a> – the configuration file of every Node.js app, created with npm or yarn, or any other package manager that uses the same approach.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/README.md">README.md</a> – not much to say here – it's a simple text file that uses Markdown and contains the description of the project, plus any other information you want to put there.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/vite.config.js">vite.config.js</a> – the main configuration file that Vite uses, which you get when you do the installation from the previous step. The content of this file by default looks like this:</p>
</li>
</ul>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { defineConfig } <span class="hljs-keyword">from</span> <span class="hljs-string">'vite'</span>
<span class="hljs-keyword">import</span> react <span class="hljs-keyword">from</span> <span class="hljs-string">'@vitejs/plugin-react'</span>

<span class="hljs-comment">// &lt;https://vitejs.dev/config/&gt;</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> defineConfig({
  <span class="hljs-attr">plugins</span>: [react()],
})
</code></pre>
<p>But in my case, I had to add the <code>test</code> configurations so that we could run the tests. You will read more about this later in the article.</p>
<ul>
<li><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/setupTests.js">setupTests.js</a> – this one is used for configuring the unit tests. We put here any of the things we would like to have available in the tests we write.</li>
</ul>
<p>For example, in order to be able to use unique keys when rendering multiple elements (<a target="_blank" href="https://react.dev/learn/rendering-lists#where-to-get-your-key">because we React needs it</a>), I am using the <code>randomUUID()</code>method of the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto">Crypto</a> interface to generate a v4 <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/UUID">UUID</a> using a cryptographically secure random number generator. And to make it available in my tests, I need to add it here, like this:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { expect, afterEach } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>;
<span class="hljs-keyword">import</span> { cleanup } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> matchers <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/jest-dom/matchers'</span>;
<span class="hljs-keyword">import</span> {randomUUID} <span class="hljs-keyword">from</span> <span class="hljs-string">'node:crypto'</span>;

<span class="hljs-comment">// extends Vitest's expect method with methods from react-testing-library</span>
expect.extend(matchers);

<span class="hljs-comment">// runs a cleanup after each test case (e.g. clearing jsdom)</span>
afterEach(<span class="hljs-function">() =&gt;</span> {
    cleanup();
});

<span class="hljs-built_in">window</span>.crypto.randomUUID = randomUUID;
</code></pre>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/yarn.lock">yarn.lock</a> – this is generated automatically when run yarn installation and lock the version of the packages being used.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/.gitignore">.gitignore</a> – comes out of the box from Vite installation. Here you define which files and folders you want Git to ignore, that is not get committed to your repo.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/index.html">index.html</a> – this the app’s entry point. It's a simple HTML document that has a few meta tags, and includes the logo and the main script file.</p>
</li>
<li><p>/src – contains a few different things we need to discuss:</p>
</li>
</ul>
<ol>
<li><p>First, it has the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/main.jsx">main.jsx</a> file, which is where React and ReactDOM come in play. We also load here the default styles file I mentioned earlier.</p>
</li>
<li><p>It also has <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/index.css">index.css</a> which I have explained already.</p>
</li>
<li><p>Then we have <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/App.jsx">App.jsx</a> which is where our actual application code begins. This file can be considered as the main component in our application, as it contains all ‘inner’ parts of our app.</p>
</li>
<li><p>Then we have <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/app.module.scss">app.module.scss</a> which contains styles for App component, using the CSS module convention to name the files with ‘module’ prefix and ‘scss’ extension.</p>
</li>
<li><p>Finally, we have <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/App.test.jsx">App.test.jsx</a> which contains a simple test for App component, using <a target="_blank" href="https://vitest.dev">Vitest</a> for testing framework.</p>
</li>
</ol>
<h3 id="heading-components">🛠️ Components</h3>
<p>Let me walk you quickly through each of the components in the application. They are located in the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components">components</a> folder.</p>
<h4 id="heading-components-folder-contents">/components folder contents:</h4>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Footer">Footer</a> – self-explanatory, contains the footer part of the app.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/GameStatus">GameStatus</a> – used to show if a game has started, that is if it's being played.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Header">Header</a> – self-explanatory, contains the header part of the app.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/MessageBoard">MessageBoard</a> – a small component used to display text messages that state when the games are starting or if we are looking at the “Summary” screen or the “Current Games”.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Result">Result</a> – another small component showing game scores.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Scoreboard">Scoreboard</a> – kind a <em>parent</em> component, serving as container that holds all the small ones in place.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/ScoreboardsGrid">ScoreboardGrid</a> – this is the most important component in the app, as it contains all the logic related to the timers. It holds all child components and it’s responsible for passing the necessary data to them via their props.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/TeamView">TeamView</a> – another small component serving as a representation of a team, which shows team’s flag and name.</p>
</li>
</ul>
<h3 id="heading-timeouts">⏱️ Timeouts</h3>
<p>The timeouts – or more precisely the time intervals – in the application are implemented with the help of several React hooks. All of them are located in the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/hooks">hooks</a> folder. I borrowed them from a very knowledgable and kinda famous guy named Josh W Comeau. I'll post the links in the end of the article.</p>
<p>So basically we use three hooks, one per type of time interval or timeout we need.</p>
<ol>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/hooks/useInterval.js">useInterval</a> – this is based on the built-in JavaScript <code>setInterval</code> function and it's used for initial countdown, before the games start</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/hooks/useRandomInterval.js">useRandomInterval</a> – this is an enhanced version of the previous one, and it’s used for randomly updating the score of the games, as well as randomly starting and stopping them</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/hooks/useTimeout.js">useTimeout</a> – this is based on the built-in JavasScript <code>setTimeout</code> function and it’s used for deciding when to stop the playing time of the games and start finalizing them</p>
</li>
</ol>
<h2 id="heading-how-to-build-the-project">🧾 How to Build the Project</h2>
<p>By now you should have a decent understanding of what our application is and how its various parts are put together.</p>
<p>Let me now guide you, step-by-step, from the very beginning, and show you how I built it. I will add images where necessary, so that it’s easier for all of you to follow along.</p>
<h3 id="heading-dependencies">📦 Dependencies</h3>
<p>The dependencies we have are very few. Except Vite and Vitest, I have installed additionally only SASS, and the React Testing Library. Here is how my <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/package.json">package.json</a> file looks:</p>
<pre><code class="lang-jsx">{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"scoreboard"</span>,
  <span class="hljs-string">"private"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>,
  <span class="hljs-string">"author"</span>: <span class="hljs-string">"Mihail Gaberov"</span>,
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"dev"</span>: <span class="hljs-string">"vite"</span>,
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"vitest"</span>,
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"vite build"</span>,
    <span class="hljs-string">"preview"</span>: <span class="hljs-string">"vite preview"</span>
  },
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"react"</span>: <span class="hljs-string">"^18.2.0"</span>,
    <span class="hljs-string">"react-dom"</span>: <span class="hljs-string">"^18.2.0"</span>
  },
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-string">"@testing-library/jest-dom"</span>: <span class="hljs-string">"^5.16.5"</span>,
    <span class="hljs-string">"@testing-library/react"</span>: <span class="hljs-string">"^14.0.0"</span>,
    <span class="hljs-string">"@types/react"</span>: <span class="hljs-string">"^18.0.28"</span>,
    <span class="hljs-string">"@types/react-dom"</span>: <span class="hljs-string">"^18.0.11"</span>,
    <span class="hljs-string">"@vitejs/plugin-react"</span>: <span class="hljs-string">"^3.1.0"</span>,
    <span class="hljs-string">"jsdom"</span>: <span class="hljs-string">"^21.1.1"</span>,
    <span class="hljs-string">"sass"</span>: <span class="hljs-string">"^1.59.3"</span>,
    <span class="hljs-string">"vite"</span>: <span class="hljs-string">"^4.2.0"</span>,
    <span class="hljs-string">"vitest"</span>: <span class="hljs-string">"^0.29.7"</span>
  }
}
</code></pre>
<h3 id="heading-installation">🧑🏻‍💻 Installation</h3>
<p>In this step I assume you are starting from scratch. We are going to use Vite for scaffolding the project. In order to do that you need to have Node.js installed on your system – at least version 14..18. I suggest that you update it to the latest stable version. And as a package manager you may go with either <a target="_blank" href="https://docs.npmjs.com/downloading-and-installing-node-js-and-npm">npm</a> or <a target="_blank" href="https://classic.yarnpkg.com/lang/en/docs/install/">yarn</a>. In my case I use yarn.</p>
<p>In your terminal app run the following:</p>
<pre><code class="lang-jsx">yarn create vite
</code></pre>
<p>And then follow the prompt.</p>
<p>Some of you may ask “Why Vite?” Here is a <a target="_blank" href="https://cloudfour.com/thinks/in-praise-of-vite/">little praise</a> of Vite that should answer that question.</p>
<p>After doing the installation we have the bare skeleton of a React app that we can start building on. Here is how it looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/default-vite-project.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Default Vite Project</em></p>
<p>💡 When starting such projects, I usually wipe off what’s there by default. Meaning that I delete the files I don’t plan to use, clean up App.jsx, and update the index.html file.</p>
<p>Another thing you may have noticed already is that the only pure CSS file I kept is <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/index.css">index.css</a>. This is one of the files that comes by default from Vite’s installation. I kept it as it is because it contains some basic styling that I didn’t want to move anywhere else.</p>
<p>After the initial cleaning and adding the files for styling and testing App.jsx, the project looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/first-steps.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>First steps of the project in App.jsx</em></p>
<p>In the screenshot above, you can see what the App.jsx file looks like after my changes. I placed comments as placeholders for where the components I need to create will be.</p>
<p>We are now ready to start building the components in question. Usually there are several different approaches you can take when deciding what to begin with. In this case, we'll start from top to bottom, create the header component, then jump to the scoreboard component, and in the end we'll build the footer component.</p>
<p>You could also decide to first build the essential part of the app, that is the scoreboard and in the end to add the “hat” and the “shoes”.</p>
<p>But in any case, what I recommend is to create empty components for each of the placeholders we have placed, based on the idea we have in mind about what our application will be.</p>
<p>Usually I use something called “Live Templates” in my <a target="_blank" href="https://www.jetbrains.com/webstorm/">IDE</a> (in case you are using different IDE, I am sure there is an alternative for it) that can generate different types of a boilerplate code.</p>
<p>In our case I use it for generating empty functional React components. This comes in very handy at this stage of the development process, because we can quickly create our project's components, leaving them empty. Then later we can start filling them up with content.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/live-templates-webstorm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Live Templates in Webstorm</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/generate-boilerplate.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Generating boilerplate code for the components we will create</em></p>
<p>And this is what the result of the above looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/generated-boilerplate.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Generated boilerplate code for a functional component in React</em></p>
<h3 id="heading-how-to-build-the-header">🧩 How to Build the Header</h3>
<p>In order to make the application to look more like a real-life one, I decided to add a small logo in the left part of the header, and a title next to it. Let’s see how it will look like in the browser and then how to implement it with code:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/header-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Application Header</em></p>
<p>First, I did a quick Google search and chose an appropriate image (the cup). I made sure to pick a SVG file for several reasons.</p>
<p>First and most important is the performance and the adjustability that come from it. And second, in the Vite default settings there is already an SVG logo added. So the only thing you need to do is to replace the existing one with yours. And then add some styling if necessary.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/replace-svg-logo.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>SVG logo</em></p>
<p>Let’s now look at the code of our brand new header component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> <span class="hljs-string">'./header.module.scss'</span>
<span class="hljs-keyword">const</span> Header = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">'./logo.svg'</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">'FIFA World Cup Scoreboard'</span>/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span>FIFA World Cup Scoreboard<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Header
</code></pre>
<p>If you keep the logo file in the <code>public</code> folder, you don’t need to worry about the path to the image. It’s taken care by Vite and you refer to it as it’s shown in the code above. The import statement in the beginning applies all styles to the header that make it looks like the picture.</p>
<pre><code class="lang-scss"><span class="hljs-selector-tag">header</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fdbe11</span>;
  <span class="hljs-attribute">justify-content</span>: flex-start;
  <span class="hljs-attribute">align-items</span>: center;

  <span class="hljs-selector-tag">img</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">3rem</span>;
    <span class="hljs-attribute">height</span>: auto;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">1rem</span>;
  }
}
</code></pre>
<p>After adding some tests, the content of the component folder looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/header-component-directory.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Header component directory</em></p>
<p>I mentioned in the beginning of the article that we will use Vitest and React Testing Library to write the units/components tests for this application. Here is how the tests for the header look:</p>
<pre><code class="lang-scss">import { render, screen } from '<span class="hljs-keyword">@testing-library</span>/react<span class="hljs-string">'
import { beforeEach, describe, expect, it } from '</span>vitest<span class="hljs-string">'
import Header from "./index"

describe('</span>Header<span class="hljs-string">', () =&gt; {
    beforeEach(() =&gt; {
        render(&lt;Header /&gt;)
    })
    it('</span>renders correctly the app title<span class="hljs-string">', async () =&gt; {
        expect(screen.getByText(/FIFA World Cup Scoreboard/i)).toBeVisible()
    })

    it('</span>renders correctly the app logo<span class="hljs-string">', async () =&gt; {
        const logo = screen.getByAltText('</span>FIFA World Cup Scoreboard<span class="hljs-string">');
        expect(logo).toHaveAttribute('</span>src<span class="hljs-string">', '</span>./logo.svg<span class="hljs-string">')
    })
})</span>
</code></pre>
<p>As you probably can guess just by reading the tests, what we're doing here is checking for the app title and then the logo we saw on the left.</p>
<p>Congratulations 🎉 You just finished the implementation of the first building block of your application. Let’s continue now with the main area. This is where the essential functionality of the scoreboard will be.</p>
<h3 id="heading-how-to-build-the-scoreboard">🧩 How to Build the Scoreboard</h3>
<p>The scoreboard supports two screens: one showing the scores of the games that are currently being played, and another one showing a summary of the end results.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/current-games-screen.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Current Games screen</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/summary-screen.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Summary screen</em></p>
<p>When I see this kind of layout design, I usually start thinking about a grid. Because, what’s a grid if not just rows and columns?</p>
<p>The modern CSS language has support for grid systems with just few lines of code, as you will see a bit later in this section. For example, to achieve this result I used the following styles:</p>
<pre><code class="lang-scss"><span class="hljs-selector-class">.grid</span> {
  <span class="hljs-attribute">list-style-type</span>: none;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">display</span>: grid;
  gap: <span class="hljs-number">1rem</span>;
  grid-template-<span class="hljs-attribute">columns</span>: repeat(auto-fit, minmax(<span class="hljs-number">300px</span>, <span class="hljs-number">1</span>fr));
}
</code></pre>
<p>Whenever I am about to implement a UI like this in a component based library such as React, I tend to mentally split it to separated blocks. These will turn into components.</p>
<p>Let me show you visually what I mean by this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/thinking-components.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Thinking components - visual representation</em></p>
<p>I hope you can figure out what I am showing you on the picture above.</p>
<p>This is how I mentally split the design layout we have in place into representational components.</p>
<p>After defining the constituent parts of our application, it’s time to move forward and implement them in code.</p>
<p>First we need the <code>ScoreboardsGrid</code> component that will hold all the smaller ones and will contain the logic for managing different events based on time.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/scoreboards-grid-directory.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>ScoreboardsGrid component directory</em></p>
<p>As you may have noticed, in addition to the usual files, here we have one more – <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/components/ScoreboardsGrid/ScoresReducer.js">ScoresReducer.js</a>. This is where our reducer logic lives. It’s responsible for manipulating the application state, depending on what actions are being triggered. In other words, this is where we actually update the score when a team scores, and also where we start and finish games.</p>
<p>In the return statement of the component we make use of the rest of the components we defined earlier.</p>
<pre><code class="lang-jsx">...
...
...
return (
        <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
            {timeElapsed === 0 ?
                <span class="hljs-tag">&lt;&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">MessageBoard</span> <span class="hljs-attr">message</span>=<span class="hljs-string">{getScoreBoardStateMessage()}/</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.grid}</span>&gt;</span>
                        {gamesToRender?.map(pairScore =&gt; (
                            <span class="hljs-tag">&lt;<span class="hljs-name">Scoreboard</span>
                                <span class="hljs-attr">key</span>=<span class="hljs-string">{crypto.randomUUID()}</span>
                                <span class="hljs-attr">pairScore</span>=<span class="hljs-string">{pairScore}</span>
                                <span class="hljs-attr">status</span>=<span class="hljs-string">{getGameStatus(pairScore.startedGame)}/</span>&gt;</span>))}
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/&gt;</span></span> :
                <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">MessageBoard</span> <span class="hljs-attr">message</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">Games</span> <span class="hljs-attr">are</span> <span class="hljs-attr">about</span> <span class="hljs-attr">to</span> <span class="hljs-attr">start</span> <span class="hljs-attr">in</span> ${<span class="hljs-attr">timeElapsed</span>} <span class="hljs-attr">seconds.</span>`}/&gt;</span></span>
            }
        &lt;/&gt;
    );
</code></pre>
<p>The <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/components/ScoreboardsGrid/index.jsx">rest of the code</a> consists of a few helper methods, <code>dispatch</code> methods, and the logic for starting and stopping the timers.</p>
<p>From here, things become even easier. We just are going to use our smaller components for displaying different things in our scoreboard app.</p>
<p>For example, the <code>MessageBoard</code> component is just a container that shows in a stylistic way a bit of a string, passed via its props. Here is the implementation:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">"./message-board.module.scss"</span>;

<span class="hljs-keyword">const</span> Index = <span class="hljs-function">(<span class="hljs-params">{ message }</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.message}</span>&gt;</span>
            {message}
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Index;
</code></pre>
<p>Same goes for the <code>[GameStatus](https://github.com/mihailgaberov/scoreboard/tree/main/src/components/GameStatus)</code> and <code>[Result](https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Result)</code> components. The difference between the later ones is that <code>Result</code> gets two arguments – the name of each team in a game – and displays them with a dash (’-’) in the middle. <code>GameStatus</code> is just showing whatever we pass through via its props, which happens to be a string signifying that a game is playing at the moment.</p>
<p>The only component that is a bit different is <code>TeamView</code>, because it contains both an image and text, representing the teams. The code itself is far from complicated. See for yourself:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">"./team-view.module.scss"</span>;

<span class="hljs-keyword">const</span> TeamView = <span class="hljs-function">(<span class="hljs-params">{teamData}</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.team}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">https:</span>//<span class="hljs-attr">flagcdn.com</span>/${<span class="hljs-attr">teamData.countryCode</span>}<span class="hljs-attr">.svg</span>`} <span class="hljs-attr">width</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">teamData.name</span>}`}/&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{teamData.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> TeamView;
</code></pre>
<p>Here I used a regular HTML <code>img</code> tag, setting the width using inline styling. The rest is pretty straightforward.</p>
<p>With that, our coding job is more or less done. As you have probably seen, all the component have tests. These, in most cases, are just simple checks to see whether the component is being rendered correctly.</p>
<p>Maybe the most interesting tests to talk about are the ones we added for the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/blob/main/src/components/ScoreboardsGrid/index.jsx">ScoreboardGrid</a> component.</p>
<p>This is so because we are using <a target="_blank" href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#using-waitfor-to-wait-for-elements-that-can-be-queried-with-find">React Testing Library</a> asynchronicity support to test the state of the component in different moments of the time. In this way we are able to test the initial timer ticking, before the games start. And after it expires, we can use it to check that our Current Games screen is displayed correctly. I'm pasting the code here as well, for easier reading.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>
<span class="hljs-keyword">import</span> { describe, expect, it } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>
<span class="hljs-keyword">import</span> ScoreboardsGrid <span class="hljs-keyword">from</span> <span class="hljs-string">"./index"</span>

describe(<span class="hljs-string">'ScoreboardsGrid'</span>, <span class="hljs-function">() =&gt;</span> {
    it(<span class="hljs-string">'renders correctly all available scoreboards'</span>, <span class="hljs-keyword">async</span> () =&gt; {
        render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ScoreboardsGrid</span> /&gt;</span></span>)

        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Games are about to start in 3 seconds./i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Games are about to start in 2 seconds./i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Games are about to start in 1 seconds./i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Argentina/i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Australia/i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Spain/i</span>)).toBeVisible()
        expect(<span class="hljs-keyword">await</span> screen.findByText(<span class="hljs-regexp">/Brazil/i</span>)).toBeVisible()
    })
})
</code></pre>
<p>After you've completed the implementation of the entire application and have a cup of ☕ or a glass of 🚰, it’s time to think about possible improvements.</p>
<p>For example, if we have more time to work on this project, what would we add or change, to make it an even better scoreboard app?</p>
<h3 id="heading-how-to-build-the-footer">🧩 How to Build the Footer</h3>
<p>To give the application a more complete look, I’ve decided to add a footer component as well. This is how it looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/04/footer-component.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Footer Component</em></p>
<p>The implementation of it is pretty simple, too. We have two links to social platforms and a bit of copyright text. Here is how I coded it:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> classes <span class="hljs-keyword">from</span> <span class="hljs-string">"./footer.module.scss"</span>;
<span class="hljs-keyword">import</span> packageJson <span class="hljs-keyword">from</span> <span class="hljs-string">'../../../package.json'</span>;

<span class="hljs-keyword">const</span> Footer = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> currentYear = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getFullYear();

  <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.footer}</span> <span class="hljs-attr">data-cy</span>=<span class="hljs-string">"footer"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.footerLinks}</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                <span class="hljs-attr">href</span>=<span class="hljs-string">"&lt;https://twitter.com/mihailgaberov&gt;"</span>
                <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
                <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
                <span class="hljs-attr">data-cy</span>=<span class="hljs-string">"twitterLink"</span>
            &gt;</span>
              twitter
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>{" "}
            <span class="hljs-symbol">&amp;bull;</span>{" "}
            <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                <span class="hljs-attr">href</span>=<span class="hljs-string">"&lt;https://github.com/mihailgaberov&gt;"</span>
                <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>
                <span class="hljs-attr">rel</span>=<span class="hljs-string">"noopener noreferrer"</span>
                <span class="hljs-attr">data-cy</span>=<span class="hljs-string">"githubLink"</span>
            &gt;</span>
              github
            <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.footerCopyrights}</span>&gt;</span>
            © {packageJson.author} {currentYear}. All rights reserved.
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{classes.version}</span>&gt;</span>v.{packageJson.version}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span></span>
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Footer;
</code></pre>
<p>Again, in the <a target="_blank" href="https://github.com/mihailgaberov/scoreboard/tree/main/src/components/Footer">component folder</a> you will find the other files that are necessary for applying the styles and the tests for that component.</p>
<p>The code looks a bit cluttered because I <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ul">used an unordered list</a> and added the links and the text as a separated <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li">list elements</a>. Then I used the tests to verify that the elements I want are rendered correctly.</p>
<p>Here is the essential part of the code that is doing this:</p>
<pre><code class="lang-jsx">it(<span class="hljs-string">'renders correctly social links'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    expect(screen.getByText(<span class="hljs-regexp">/twitter/i</span>)).toBeVisible()
    expect(screen.getByText(<span class="hljs-regexp">/github/i</span>)).toBeVisible()
  });

  it(<span class="hljs-string">'has social links working correctly'</span>, <span class="hljs-keyword">async</span> () =&gt; {
    expect(screen.getByText(<span class="hljs-string">'twitter'</span>).closest(<span class="hljs-string">'a'</span>)).toHaveAttribute(<span class="hljs-string">'href'</span>, <span class="hljs-string">'&lt;https://twitter.com/mihailgaberov&gt;'</span>);
    expect(screen.getByText(<span class="hljs-string">'github'</span>).closest(<span class="hljs-string">'a'</span>)).toHaveAttribute(<span class="hljs-string">'href'</span>, <span class="hljs-string">'&lt;https://github.com/mihailgaberov&gt;'</span>);
  });

  it(<span class="hljs-string">"should contain copyright info"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(screen.getByText(<span class="hljs-regexp">/© Mihail Gaberov 2023. All rights reserved./i</span>)).toBeVisible()
  });

  it(<span class="hljs-string">"should contain version number"</span>, <span class="hljs-function">() =&gt;</span> {
    expect(screen.getByText(<span class="hljs-regexp">/v.1.0.0/i</span>)).toBeVisible()
  });
</code></pre>
<h3 id="heading-possible-improvements">🚀 Possible Improvements</h3>
<ul>
<li><p>Add a clock under the game status, say counting down the seconds, to make it look more like a real-time app</p>
</li>
<li><p>Add some animation when updating the scores to make it easier for the user to spot the change</p>
</li>
<li><p>Add some interactivity in general:</p>
<ul>
<li><p>Clicking on each game leads to a details pane with the match details</p>
</li>
<li><p>Option for selecting a favourite team</p>
</li>
<li><p>Add another page/tab where users can read a history summary of the past games</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>That was a fun challenge. Especially if you are a football fan, right? ⚽</p>
<p>We learned about several interesting topics.</p>
<p>First we learned what a scoreboard is, and the ‘why’ and the ‘how’ behind it.</p>
<p>Then we learned about Vite and Vitest, which I think are currently the best tools to use when making a React app – especially if you don’t want to start from scratch and deal with Webpack manually.</p>
<p>Then we saw how to leverage JavaScript timeouts with React hooks, and thus create some kind of time-based interactivity.</p>
<p>And last, but not least, we had fun, didn't we? 🕺🏻</p>
<p>Thanks for reading 🙏🏻</p>
<h2 id="heading-references"><strong>References:</strong></h2>
<ul>
<li><p>Resources from Josh W. Comeau: for <a target="_blank" href="https://www.joshwcomeau.com/snippets/react-hooks/use-interval/">use-interval</a> and <a target="_blank" href="https://www.joshwcomeau.com/snippets/react-hooks/use-timeout/">use-timeout</a> and <a target="_blank" href="https://www.joshwcomeau.com/snippets/react-hooks/use-random-interval/">use-random-interval</a></p>
</li>
<li><p>An article from Kent C. Dodds about <a target="_blank" href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#using-waitfor-to-wait-for-elements-that-can-be-queried-with-find">common mistakes devs make with the React Testing Library</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Convert Arabic Numbers to Roman Numerals with SolidJS ]]>
                </title>
                <description>
                    <![CDATA[ Have you heard about the Romans? Who hasn’t, right 🙂 They used their own numeric system, which was a bit of a mouthful, especially when it came to writing. It looks like this: I, II, III, IV, V, VI and so on. Maybe that’s one of the reasons that peo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-convert-arabic-numbers-to-roman-numerals-with-solidjs/</link>
                <guid isPermaLink="false">66d4604c33b83c4378a5180a</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Wed, 08 Mar 2023 20:52:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/03/Arabic_to_Roman_Converter_-_Mihail_Gaberov.gif" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you heard about the Romans? Who hasn’t, right 🙂</p>
<p>They used their own numeric system, which was a bit of a mouthful, especially when it came to writing. It looks like this: <strong>I, II, III, IV</strong>, <strong>V</strong>, <strong>VI</strong> and so on.</p>
<p>Maybe that’s one of the reasons that people adopted and started using the Arabic numeric system. It's the one we all know and use on a daily basis. Yes, yes, the same – 1,2,3…and so on.</p>
<h2 id="heading-what-are-we-building"><strong>What Are We Building?</strong></h2>
<p>In this tutorial, we will see how to build a small app that gives the user an input for entering Arabic numbers and displays their Roman numeral equivalent with a nice, sleek animation.</p>
<p>We will use <a target="_blank" href="https://www.solidjs.com/">SolidJS</a> for building the UI and good old <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/javascript">JavaScript</a> for implementing the algorithm for the actual conversion. More on this later in the article.</p>
<p>We also will take advantage of <a target="_blank" href="https://css-tricks.com/css-modules-part-1-need/">CSS Modules</a> and <a target="_blank" href="https://sass-lang.com/">SASS</a> to help make our application a bit more eye pleasing.</p>
<h2 id="heading-github-repo-and-demo-project">GitHub Repo and Demo Project</h2>
<p>💡If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/arabic-roman-visualized/">here</a> 💁 is the GitHub repository, and here you can see the live <a target="_blank" href="https://arabic-roman-visualized.vercel.app/">demo</a> 📺.</p>
<h2 id="heading-what-is-solidjs">What is SolidJS?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/logo.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>SolidJS is a front-end library for creating reactive user interfaces. It is still relatively new. It looks a lot like <a target="_blank" href="https://beta.reactjs.org/">React</a>, but they say it’s <a target="_blank" href="https://www.webtips.dev/solidjs-vs-react">simpler and faster</a>.</p>
<p>It got my attention recently, so I’ve decided to take a deeper look and educate myself. And, of course, share my experience with you here.</p>
<h2 id="heading-the-project">The Project</h2>
<p>Our application is really simple. It has just a few dependencies and contains only several components. Let me walk you briefly through them.</p>
<h3 id="heading-dependencies">Dependencies</h3>
<pre><code class="lang-json">{
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"vite"</span>: <span class="hljs-string">"^4.1.1"</span>,
    <span class="hljs-attr">"vite-plugin-solid"</span>: <span class="hljs-string">"^2.5.0"</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"@motionone/solid"</span>: <span class="hljs-string">"^10.15.5"</span>,
    <span class="hljs-attr">"@solid-primitives/keyed"</span>: <span class="hljs-string">"^1.1.8"</span>,
    <span class="hljs-attr">"sass"</span>: <span class="hljs-string">"^1.58.3"</span>,
    <span class="hljs-attr">"solid-js"</span>: <span class="hljs-string">"^1.6.10"</span>
  }
}
</code></pre>
<p>Except the obvious dependency – <code>solid-js</code> – I’ve only installed the <code>sass</code>, <code>@motionone/solid</code> and <code>@solid-primitives/keyed</code> libraries. We will use these for the styling and the animations. <a target="_blank" href="https://vitejs.dev/">Vite</a>-related packages come with the SolidJS app installation, which means that when you run this:</p>
<p><code>npx degit solidjs/templates/js my-app</code></p>
<p>It will install all you need to initially run your new SolidJS app.</p>
<h3 id="heading-project-structure">Project Structure</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/Untitled.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Project Structure</em></p>
<p>Maybe folks with React experience will see immediately how much this looks like a regular React application. We have the same file/folders organization. And yes, we can use JSX with Solid as well.</p>
<p>After seeing the ‘new’ Solid application structure, let's dive into the basic components that come from the library and that we need in order to build the UI.</p>
<h3 id="heading-components">Components</h3>
<p>Components in Solid are, surprise surprise 😲, regular JavaScript functions. A Solid app is composed of these functions. And, same way as in React, they support <a target="_blank" href="https://beta.reactjs.org/learn/writing-markup-with-jsx">JSX</a>. This means we can write functions that produce DOM elements.</p>
<p>For example, here is how our <a target="_blank" href="https://github.com/mihailgaberov/arabic-roman-visualized/tree/main/src/components/Logo">Logo</a> component looks:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> styles <span class="hljs-keyword">from</span> <span class="hljs-string">'./Logo.module.scss'</span>;
<span class="hljs-keyword">import</span> gameLogo <span class="hljs-keyword">from</span> <span class="hljs-string">'../../../assets/logo.png'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Logo</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{styles.logo}</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{gameLogo}</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"logo"</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>Yes, you guessed right. This will produce HTML code like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/Untitled-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Logo component (HTML)</em></p>
<p>Maybe some of you are already asking “C’mon Mihail, are you kidding? What’s the difference between Solid and React?” and I agree. So far everything looks pretty much the same. Let’s talk about Signals now.</p>
<h3 id="heading-solidjs-signals">SolidJS Signals</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/SolidJS-Create-Signal-Component-2-1270x762.jpeg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>SolidJS Signal</em></p>
<p>Signals are the foundation of reactivity in Solid. They hold values that change over time and they update everything else that uses these values. Which means that if you change a signal’s value, this change will be propagated everywhere else in your app where it’s being used.</p>
<p>This is something that’s missing in the React world. At least in name.</p>
<p>When a signal is created, it gives you back two functions, a getter and setter. You use the first one to get the current value the signal contains. And the setter function is used to change that value. The syntax is the following:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> [count, setCount] = createSignal(<span class="hljs-number">0</span>)
</code></pre>
<p>The value you pass as an argument to <code>createSignal</code> is the initial value that will be held by the signal, 0 in this case. This means that if you call <code>count()</code>without calling <code>setCount</code> with a different value before that, the result you will get is that zero.</p>
<p>Now that you've seen how to use it and what its purpose is, you might be thinking about its equivalent in React, <code>useState</code>. In my case, that was the initial association that popped into my mind. The so-called signals are means to manage the state in a Solid application. As it gives you an easy way for accessing and changing it.</p>
<h2 id="heading-the-algorithm">The Algorithm</h2>
<p>There are multiple implementations of this algorithm. And in many different programming languages. Only a Google search can say how many 🤓.</p>
<p>We'll implement it in JavaScript. I keep the file containing the algorithm separate, in a directory called <a target="_blank" href="https://github.com/mihailgaberov/arabic-roman-visualized/tree/main/lib">lib</a>. This approach keeps our hands untied when it comes to replacing the UI with, say, one done with a different UI library. Or maybe we use it in completely different context. That is, the <code>frontend</code> and the <code>backend</code> of our application are totally decoupled.</p>
<p>Let’s first walk through the algorithm itself and then we can discuss some improvements we can do.</p>
<h3 id="heading-algorithm-steps">Algorithm Steps</h3>
<p>First things first, let me layout the code here so that it’s easier for you to follow:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> convertArabicToRoman = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">num</span>) </span>{
    <span class="hljs-keyword">const</span> rules = {
        <span class="hljs-string">"M"</span>: <span class="hljs-number">1000</span>,
        <span class="hljs-string">"CM"</span>: <span class="hljs-number">900</span>,
        <span class="hljs-string">"D"</span>: <span class="hljs-number">500</span>,
        <span class="hljs-string">"CD"</span>: <span class="hljs-number">400</span>,
        <span class="hljs-string">"C"</span>: <span class="hljs-number">100</span>,
        <span class="hljs-string">"XC"</span>: <span class="hljs-number">90</span>,
        <span class="hljs-string">"L"</span>: <span class="hljs-number">50</span>,
        <span class="hljs-string">"XL"</span>: <span class="hljs-number">40</span>,
        <span class="hljs-string">"XXX"</span>: <span class="hljs-number">30</span>,
        <span class="hljs-string">"XX"</span>: <span class="hljs-number">20</span>,
        <span class="hljs-string">"X"</span>: <span class="hljs-number">10</span>,
        <span class="hljs-string">"IX"</span>: <span class="hljs-number">9</span>,
        <span class="hljs-string">"V"</span>: <span class="hljs-number">5</span>,
        <span class="hljs-string">"IV"</span>: <span class="hljs-number">4</span>,
        <span class="hljs-string">"I"</span>: <span class="hljs-number">1</span>
    }

    <span class="hljs-keyword">let</span> res = <span class="hljs-string">""</span>;
    <span class="hljs-keyword">const</span> romans = <span class="hljs-built_in">Object</span>.keys(rules);

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; romans.length; ++i) {
        <span class="hljs-keyword">const</span> val = rules[romans[i]];

        <span class="hljs-keyword">while</span> (num &gt;= val) {
            num -= val;
            res += romans[i];
        }
    }
    <span class="hljs-keyword">return</span> res;
};
</code></pre>
<p>Next, let’s take a look at the rules that define how the Roman numerals are created.</p>
<p>These rules allow you to write down any number:</p>
<ul>
<li><p>If a smaller numeral comes after a larger numeral, add the smaller number to the larger number.</p>
</li>
<li><p>If a smaller numeral comes before a larger numeral, subtract the smaller number from the larger number.</p>
</li>
<li><p>Do not use the same symbol more than three times in a row.</p>
</li>
<li><p>Modern usage employs seven symbols, each with a fixed integer value:</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/image-57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Keeping in mind these rules, here are few examples:</p>
<ul>
<li><p>399 in Roman Numerals is <strong>CCCXCIX</strong></p>
</li>
<li><p>151 in Roman Numerals is <strong>CLI</strong></p>
</li>
<li><p>185 in Roman Numerals is <strong>CLXXXV</strong></p>
</li>
<li><p>3070 in Roman Numerals is <strong>MMMLXX</strong></p>
</li>
<li><p>570 in Roman Numerals is <strong>DLXX</strong></p>
</li>
<li><p>7 in Roman Numerals is <strong>VII</strong></p>
</li>
<li><p>290 in Roman Numerals is <strong>CCXC</strong></p>
</li>
<li><p>1880 in Roman Numerals is <strong>MDCCCLXXX</strong></p>
</li>
<li><p>47 in Roman Numerals is <strong>XLVII</strong></p>
</li>
</ul>
<p><strong>Let’s go through our solution now step by step:</strong> 🎢.</p>
<ol>
<li><p>Create a data structure that will hold the representations of the known numbers from the rules. In our case it is a simple JavaScript object.</p>
</li>
<li><p>Add a few more known numbers that will help us in the later calculations, that is 4, 9, 20, 30, 40, 90, 400 and 900.</p>
</li>
<li><p>Create an empty string that will hold the result.</p>
</li>
<li><p>Then use the Object.keys() method to get all Roman numerals from our structure.</p>
</li>
<li><p>Iterate through them via a for-loop.</p>
</li>
<li><p>For each Roman numeral letter, get its Arabic counterpart and check if it’s less than or equal to the number we are converting.</p>
</li>
<li><p>If it is, first subtract it from the number we are converting and then store the current Roman representation in the <code>res</code> string by concatenating it with what’s already there.</p>
</li>
<li><p>After both loops finish, return the end result in our string variable.</p>
</li>
</ol>
<p>After defining the algorithm steps, let’s run a specific example through them and it will get clearer.</p>
<h3 id="heading-example">Example</h3>
<p>OK, let’s take a random number and pass it to our algorithm. Say number <strong>1293</strong>. We will skip the <strong>preparation</strong> steps and go straight to where the real magic happens. Which means we start with getting the Romans numerals which are the keys of our key-value data structure:</p>
<pre><code class="lang-python">    const romans = Object.keys(rules); // [<span class="hljs-string">"M"</span>, <span class="hljs-string">"CM"</span>, <span class="hljs-string">"D"</span>, <span class="hljs-string">"CD"</span>, <span class="hljs-string">"C"</span>, <span class="hljs-string">"XC"</span>, <span class="hljs-string">"L"</span>, <span class="hljs-string">"XL"</span>, <span class="hljs-string">"XXX"</span>, <span class="hljs-string">"XX"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"IX"</span>, <span class="hljs-string">"V"</span>, <span class="hljs-string">"IV"</span>, <span class="hljs-string">"I"</span>]
</code></pre>
<p>This results in an array holding the Roman numeral representations we have there. That allows us to iterate over them via the **for-**loop and access each of them on every cycle.</p>
<p>Then, having access to each Roman numeral from this array, we then get its value:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> val = rules[romans[i]]; <span class="hljs-comment">// First cycle this will give 1000, 900 in the second, and so on</span>
</code></pre>
<p>So we have number 1293 as our input, which we named <code>num</code>. In the inner <strong>while-</strong> loop we compare the input with the currently selected value (from the <code>rules</code> data structure) and if it’s bigger or equal, we subtract it from our input value. Then we concatenate the Roman numeral letter to our string result.</p>
<p>In our example that would mean the following:</p>
<pre><code class="lang-python">Is <span class="hljs-number">1293</span> &gt;= <span class="hljs-number">1000</span> &gt; yes =&gt; num = <span class="hljs-number">1293</span> - <span class="hljs-number">1000</span> = <span class="hljs-number">293</span> <span class="hljs-keyword">and</span> res = <span class="hljs-string">'M'</span>
</code></pre>
<p>Then we keep iterating with the result value from the previous iteration.</p>
<pre><code class="lang-python">Is <span class="hljs-number">293</span> &gt;= <span class="hljs-number">1000</span> =&gt; no =&gt; <span class="hljs-number">293</span> &gt;= <span class="hljs-number">900</span> =&gt; no =&gt; <span class="hljs-number">293</span> &gt;= <span class="hljs-number">500</span> =&gt; no
=&gt; <span class="hljs-number">293</span> &gt;= <span class="hljs-number">400</span> =&gt; no =&gt; <span class="hljs-number">293</span> &gt;= <span class="hljs-number">100</span> =&gt; yes =&gt; <span class="hljs-number">293</span> - <span class="hljs-number">100</span> = <span class="hljs-number">193</span>
<span class="hljs-keyword">and</span> res = <span class="hljs-string">'MC'</span>
</code></pre>
<pre><code class="lang-python">Is <span class="hljs-number">193</span> &gt;= <span class="hljs-number">100</span> =&gt; yes =&gt; <span class="hljs-number">193</span> - <span class="hljs-number">100</span> = <span class="hljs-number">93</span> <span class="hljs-keyword">and</span> res = <span class="hljs-string">'MCC'</span>
</code></pre>
<pre><code class="lang-python">Is <span class="hljs-number">93</span> &gt;= <span class="hljs-number">100</span> =&gt; no =&gt; <span class="hljs-number">93</span> &gt;= <span class="hljs-number">90</span> =&gt; yes =&gt; <span class="hljs-number">93</span> - <span class="hljs-number">90</span> = <span class="hljs-number">3</span> <span class="hljs-keyword">and</span> res = <span class="hljs-string">'MCCXC'</span>
</code></pre>
<pre><code class="lang-python">Is <span class="hljs-number">3</span> &gt;= <span class="hljs-number">90</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">50</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">40</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">30</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">20</span>
=&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">10</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">9</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">5</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">4</span> =&gt; no =&gt; <span class="hljs-number">3</span> &gt;= <span class="hljs-number">1</span>
=&gt; yes =&gt; <span class="hljs-number">3</span> - <span class="hljs-number">1</span> = <span class="hljs-number">2</span> <span class="hljs-keyword">and</span> res = <span class="hljs-string">'MCCXCI'</span>
</code></pre>
<pre><code class="lang-python">Is <span class="hljs-number">2</span> &gt;= <span class="hljs-number">1</span> =&gt; yes =&gt; <span class="hljs-number">2</span> - <span class="hljs-number">1</span> = <span class="hljs-number">1</span> <span class="hljs-keyword">and</span> res = <span class="hljs-string">'MCCXCII'</span>
</code></pre>
<p>and lastly</p>
<pre><code class="lang-python">Is <span class="hljs-number">1</span> &gt;= <span class="hljs-number">1</span> =&gt; yes =&gt; <span class="hljs-number">1</span> - <span class="hljs-number">1</span> = <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> res = <span class="hljs-string">'MCCXCIII'</span> <span class="hljs-keyword">is</span> the final result! 🎉🎉🎉
</code></pre>
<h3 id="heading-how-to-improve-the-process">How to Improve the Process</h3>
<p>One thing is important to mention here. If we use the algorithm directly as it is, it could consume whatever number we pass to it. But, keeping in mind the rules mentioned above, the result string could get really long. This could break the UI or at the very least it would make it ugly and unusable for writing.</p>
<p>This is why some implementations talk about adding more letters to the rules. As shown on the table below, we could have letters signifying bigger numbers. Adding this to the algorithm would significantly reduce the length of the result string when a bigger number is added.</p>
<p>For example, with our current implementation, if we enter 1,000,000, the result would be 1000 times the letter M. You can imagine it, it’s a long string of Ms, such as MMMMMMMM_…_ But, if we introduce additional letters for bigger numbers, this will become just the letter M with a line above, as shown in the table below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/03/Untitled-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Possible improvement using more letters. Source: by</em> <a target="_blank" href="https://www.calculateme.com/roman-numerals/to-roman"><em>https://www.calculateme.com/roman-numerals/to-roman</em></a></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Yet another learning-by-sharing session ends here 🎉.</p>
<p>We achieved two main goals in this tutorial. First we touched on a relatively new player on the front-end libraries market, SolidJS. We got familiar with its basic elements. We figured out how to use them in order to quickly build a decent user interface. And we’ve manage to use this UI to show the workings of our algorithm.</p>
<p>And second, we tackled the algorithm itself. We saw how can we implement it in JavaScript. We also know understand the limitations of this approach, and what would be possible improvements. For example, you can add more letters for signifying the bigger numbers. This could easily remove our top limit. And instead of 4999 we could go unlimited.</p>
<p>Last thing I would like to say here is, as always, thank you for reading 🙏🏻, I hope it was fun and interesting for you!</p>
<h3 id="heading-references">References:</h3>
<ul>
<li><a target="_blank" href="https://www.solidjs.com/">SolidJS docs</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Solve the Parking Lot Challenge in JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Have you heard about the Parking Lot challenge? If not, let me explain briefly. The Parking Lot is challenge where you are asked to write a class that manages an imaginary parking lot. In this tutorial we will do that in JavaScript. And to make it a ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/parking-lot-challenge-solved-in-javascript/</link>
                <guid isPermaLink="false">66d4605ae39d8b5612bc0ddb</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Thu, 16 Jun 2022 14:55:53 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/06/parking-loot.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you heard about the Parking Lot challenge? If not, let me explain briefly.</p>
<p>The Parking Lot is challenge where you are asked to write a class that manages an imaginary parking lot.</p>
<p>In this tutorial we will do that in JavaScript. And to make it a bit more interesting, we will create a small React app that will visualize the workings of our class.</p>
<p>Let's begin. 🎉</p>
<h1 id="heading-challenge-requirements">Challenge Requirements</h1>
<p>For this challenge, you have to implement a class in JavaScript. That class should consist of variables and methods that simulate how a parking lot works. Here are the details:</p>
<ul>
<li><p>We should be able to create the parking lot with a given size (number of parking spots)</p>
</li>
<li><p>We don’t differentiate between different vehicles – we consider them all the same</p>
</li>
<li><p>Our class provides a method for parking new cars in the parking lot</p>
</li>
<li><p>Our class provides a method for removing already parked cars, and</p>
</li>
<li><p>Our class provides a method for getting the size of the parking lot (total count of slots)</p>
</li>
</ul>
<h1 id="heading-parking-lot-challenge-solution">Parking Lot Challenge Solution</h1>
<p>First, let’s take a look at the class logic itself.</p>
<p>It’s pretty straightforward, so there probably won’t be any surprises for most of you – especially if you already have some programming experience in OOP and class-based languages.</p>
<h2 id="heading-class-parkinglot"><strong>class ParkingLot</strong></h2>
<p>I will give you the code first and then I'll follow up with a short explanation on the implementation.</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ParkingLot</span> </span>{
  slots = [];

  <span class="hljs-keyword">constructor</span>(parkingSize) {
    <span class="hljs-built_in">this</span>.slots = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(parkingSize).fill(<span class="hljs-literal">null</span>);
  }

  park(carId) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Parking car: <span class="hljs-subst">${carId}</span>`</span>);
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.slots.every(<span class="hljs-function">(<span class="hljs-params">slot</span>) =&gt;</span> slot !== <span class="hljs-literal">null</span>)) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt;= <span class="hljs-built_in">this</span>.slots.length; i++) {
      <span class="hljs-keyword">const</span> slot = <span class="hljs-built_in">this</span>.slots[i];

      <span class="hljs-keyword">if</span> (slot === <span class="hljs-literal">null</span>) {
        <span class="hljs-built_in">this</span>.slots[i] = carId;
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    }
  }

  remove(carId) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Leaving car: <span class="hljs-subst">${carId}</span>`</span>);
    <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.slots.every(<span class="hljs-function">(<span class="hljs-params">slot</span>) =&gt;</span> slot !== carId)) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt;= <span class="hljs-built_in">this</span>.slots.length; i++) {
      <span class="hljs-keyword">const</span> slot = <span class="hljs-built_in">this</span>.slots[i];

      <span class="hljs-keyword">if</span> (slot === carId) {
        <span class="hljs-built_in">this</span>.slots[i] = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    }
  }

  getSlots() {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Parking slots: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.slots}</span>`</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.slots;
  }

  getSize() {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Parking size is: <span class="hljs-subst">${<span class="hljs-built_in">this</span>.slots.length}</span>`</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.slots.length;
  }

  getAvailable() {
    <span class="hljs-keyword">const</span> availableSlots = <span class="hljs-built_in">this</span>.slots.filter(<span class="hljs-function">(<span class="hljs-params">s</span>) =&gt;</span> s === <span class="hljs-literal">null</span>).length;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Available parking slots: <span class="hljs-subst">${availableSlots}</span>`</span>);
    <span class="hljs-keyword">return</span> availableSlots;
  }

  isFull() {
    <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.getAvailable() === <span class="hljs-number">0</span>;
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> ParkingLot;
</code></pre>
<p>Starting from the beginning – our class has one property, <code>slots</code>, which is going to be an array that stores info about the parking slots (whether they're free or occupied).</p>
<p>Then we have a <code>constructor</code> method that gets executed every time you create an instance of this class. Here is where we use an input number parameter, called <code>parkingSize</code>, to create an empty array with length equal to that number.</p>
<p>Technically speaking, this array is not empty, as we initialize it with <em>null</em> values. This means that after the code executes in the constructor, we will end up with an array filled with null values, depending on the number we passed in.</p>
<p>For example, if we execute this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> parking = <span class="hljs-keyword">new</span> ParkingLot(<span class="hljs-number">5</span>);
</code></pre>
<p>It will result in this:</p>
<pre><code class="lang-javascript">[<span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>, <span class="hljs-literal">null</span>] <span class="hljs-comment">// lenght = 5</span>

instead <span class="hljs-keyword">of</span> [] <span class="hljs-comment">// empty array, length 0</span>
</code></pre>
<p>After going through the constructor, let’s take a look at the rest of the methods in the class.</p>
<p><code>park()</code> – This is where we do the actual parking of a car. This method iterates over the <code>slots</code> array, checks if there are free spots (that is, slots that are still equal to null), and adds in the car in those empty spots.</p>
<p>Cars are given by <code>carId</code>. This is just an identifier we use to signify that we have a car in a certain spot. Note that this method returns false if there are no free slots or true if parking was successful.</p>
<p><code>getSlots()</code> – Helper method that just returns the array we use to store the parking slots.</p>
<p><code>remove()</code> – This is how we remove cars from the parking lot. This method also iterates over the slots array.</p>
<p>💡As you may have noticed up until now, in almost every case when we need to manipulate data stored in a data structure like array, we have to iterate over this structure so that we can access its elements.</p>
<p>Different programming languages provide different data structures and methods to work with them, but the main idea is always the same: when you need to do something with this data, you need to iterate over it in some way.</p>
<p>To remove a car from the parking lot, we use the aforementioned identifier. We look for such items in the slots array, and if we get a match, we have a car to ‘un-park’. We perform the actual removal by just setting that specific slot to <em>null</em> again.</p>
<p>Now you can guess why we decided to initialize our slots array with nulls in the first place.</p>
<p>This method also returns a boolean result depending on whether there was a successful removal or not.</p>
<p>We should be able to use this feedback when building some kind of UI that is able to react to such changes. The same is valid when adding cars to the parking lot (look at the <code>park</code> method).</p>
<p><code>getSize()</code> – Another helper method that we use to check the parking lot size.</p>
<p><code>getAvailable()</code> – This one shows us how many available slots we currently have.</p>
<p><code>isFull()</code> – Tells us if the parking lot is full, that is that there are no more available slots.</p>
<h1 id="heading-how-to-build-the-react-app">How to Build the React App</h1>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-92.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Parking Lot app - main screen</em></p>
<p>Here is where the fun starts. 🕺</p>
<p>We are going to create an interactive app, visualizing the tasks which we can perform with the help of our implementation above.</p>
<p>Our app will provide basic UI controls allowing an (imaginary) operator to work with the parking lot software_._ And in order to make their work a bit more pleasing to the eye, we will try to animate the basic functions our software provides.</p>
<p>Let’s see how! 📺</p>
<h2 id="heading-demo">Demo</h2>
<p>Here is the live demo for those of you who doesn’t care about the details and just want to ‘taste’ it: 🥪</p>
<p><a target="_blank" href="https://parking-lot-chi.vercel.app/">Embedded content</a></p>
<h2 id="heading-source-code">Source Code</h2>
<p>Here is the <a target="_blank" href="https://github.com/mihailgaberov/parking-lot">repo</a> with the app's source code.</p>
<p>Let me give you a brief summary on the <em>what</em> and the <em>why</em>.</p>
<p>The app is built with <a target="_blank" href="https://vitejs.dev/">vite</a>. The reason for this is that I've been playing around recently with it and I am really happy with the speed and the performance it provides.</p>
<p>No matter that it’s still in the relatively early stages of development – if I am about to start a new project and am in a position to choose, I will go with <strong>vite</strong>.</p>
<p>This is not to say I have anything against its big brother <a target="_blank" href="https://create-react-app.dev/">CRA</a>. On the contrary, I have built multiple apps with it and I am still using it in some of my projects. It’s just that <strong>vite</strong> is much faster and often gives me everything I currently need.</p>
<p>💡Just keep in mind that selecting a given technology always depends on your specific needs for a specific project. That is to say that there is no silver bullet. It’s always a matter of requirements and priorities.</p>
<h2 id="heading-structure-of-the-react-app">Structure of the React App</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-93.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>App structure</em></p>
<p>The app's structure is straightforward. At the root level we have two folders – <em>assets</em> and <em>src</em>. The first contains the assets used in the app (in this case it’s just a car image). The later contains all the files with the source code.</p>
<p>Let’s take a closer look inside the source folder.</p>
<p>Here we have the following folders:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/parking-lot/tree/main/src/components">components</a> – contains all React components used in the app</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/parking-lot/tree/main/src/lib">lib</a> – contains the parking lot class, responsible for the main logic of the app</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/parking-lot/tree/main/src/pages">pages</a> – contains two sub-directories, for the two main screens in the app – Landing and Main, respectively</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/parking-lot/tree/main/src/utils">utils</a> – contains a helper method for generating fictitious car license plates that we use later when representing a parking slot as <em>busy</em></p>
</li>
<li><p>And several files, most of them are related to the entry point of the app, except the favicon ones – their role should be clear to you. If not, take a look at the tab of your browser 😉</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-94.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Browser tab with favicon</em></p>
<h2 id="heading-app-pages">App Pages</h2>
<p>As mentioned earlier, the main pages (also called screens) in the app are called <a target="_blank" href="https://github.com/mihailgaberov/parking-lot/tree/main/src/pages/Landing">Landing</a> and <a target="_blank" href="https://github.com/mihailgaberov/parking-lot/tree/main/src/pages/Main">Main</a>. These are React components themselves. They serve as skeletons for everything you see in the welcome page – where you land initially and where you can select how many parking slots you want to have in your parking lot.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-95.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Welcome page</em></p>
<p>And the page you go to after clicking the big, pink submit button - the main screen where your operator is able to manage the parking lot.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/06/image-96.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Main page</em></p>
<h2 id="heading-app-functionality"><strong>App Functionality</strong></h2>
<p>The app provides very basic functionality for managing an imaginary parking lot. When the user select how many slots they want (max 20), they'll be transitioned to the main screen. There, the user will be able to see all free parking slots.</p>
<p>When a car is parked, via the PARK! button, the relevant spot will be visualised as busy and will show the registration number of the car being parked there. The operator can un-park cars by clicking on a busy slot, that is on the car they want to “remove” from the parking lot.</p>
<h2 id="heading-the-simple-animation-of-the-moving-red-car-is-just-for-visual-effect-and-doesnt-have-any-real-influence-on-the-way-the-parking-lot-works">💡The simple animation of the moving red car is just for visual effect and doesn’t have any real influence on the way the parking lot works.</h2>
<p>I used <a target="_blank" href="https://github.com/css-modules/css-modules">CSS modules</a> for styling the app. I also tried to make the app a bit mobile friendly, in case you decide to try it on your mobile device.</p>
<p>Be my guest and <a target="_blank" href="https://parking-lot-chi.vercel.app/">give a try</a> 🧪</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>My initial idea behind this post was to describe the parking lot class itself. You know, just for educational purposes. To show you how can you write such a class in JavaScript.</p>
<p>But then I thought it’s kind of boring 🥱. I wanted to create something funnier 💃🏻, something more gamified 🕹️ so to speak.</p>
<p>And this is how I ended up with this mini game-like app 🎮.</p>
<p>While building it, my 5 year old daughter 🧒🏻 saw it and wanted to play with it. And she had a lot of fun actually!</p>
<p>Yes, yes, of course! I am not saying that if it was something funny for a 5 year old, it will be for you too 😀.</p>
<p>My only goal was to catch your attention through the game, so that the knowledge 📖 behind it stays longer with you.</p>
<p>Thanks for reading! 🙏</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ React + WebSockets Project – Build a Real-Time Order Book Application ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, we will see how to build an Order Book web application, that we'll use to display real-time cryptocurrency info. We will use React with Typescript for creating the UI, Redux for managing the application state, and styled-components ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/react-websockets-project-build-real-time-order-book-app/</link>
                <guid isPermaLink="false">66d4605e230dff0166905835</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redux ]]>
                    </category>
                
                    <category>
                        <![CDATA[ styled-components ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Tue, 19 Apr 2022 17:15:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/04/react-and-websockets-articlde.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, we will see how to build an Order Book web application, that we'll use to display real-time cryptocurrency info.</p>
<p>We will use <a target="_blank" href="https://create-react-app.dev/docs/adding-typescript/">React with Typescript</a> for creating the UI, <a target="_blank" href="https://redux.js.org/">Redux</a> for managing the application state, and <a target="_blank" href="https://styled-components.com/">styled-components</a> for applying the styling. And last, but not least, we'll use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">WebSockets</a> for fetching the data feeds.</p>
<h3 id="heading-github-repo">GitHub Repo</h3>
<p>💡 If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/orderbook">here</a> 💁 is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/README.md">README</a> 🙌, and <a target="_blank" href="https://orderbook-mihailgaberov.vercel.app/">here</a> you can see the live demo.</p>
<h2 id="heading-what-is-an-order-book">What is an Order Book?</h2>
<p>An <a target="_blank" href="https://www.coindesk.com/crypto-trading-101-how-to-read-an-exchange-order-book">Order Book</a> is an application that usually displays some kind of information related to buying and selling stuff.</p>
<p>💡 The most common use case is showing data for various assets, such as stocks, bonds, currencies, and even cryptocurrencies.</p>
<h2 id="heading-why-would-i-need-an-order-book">Why Would I Need an Order Book?</h2>
<p>In practice, Order Books are used by traders to watch the fluctuations of the bidding price and the asking price of certain products – currencies, stocks, and so on.</p>
<p>This is happening real time, so the changes can be very rapid. Here is where WebSockets will come in handy, as you will see later.</p>
<p>In the past, people did something similar on paper, but the ‘real-time' part was impossible, of course.</p>
<p>A regular Order Book usually has two sides: buying (or bidding), shown in green on the left side and selling (or asking), red, on the right.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-43.png" alt="Classic Orderbook" width="600" height="400" loading="lazy"></p>
<p><em>Classic Order book</em></p>
<h2 id="heading-the-plan-for-our-order-book-app">The Plan for our Order Book App</h2>
<p>Our Order Book app will consist of five parts:</p>
<ul>
<li><p>order book main view</p>
</li>
<li><p>grouping select box</p>
</li>
<li><p>Toggle Feed button</p>
</li>
<li><p>Kill Feed button</p>
</li>
<li><p>Status Message.</p>
</li>
</ul>
<p>The app design will look as shown below. Note that the Status Message component, that you will see in the my implementation, is missing on these screenshots:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-60.png" alt="Desktop layout" width="600" height="400" loading="lazy"></p>
<p><em>Desktop layout</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-61.png" alt="Mobile layout" width="600" height="400" loading="lazy"></p>
<p><em>Mobile layout</em></p>
<h2 id="heading-application-features"><strong>Application Features</strong></h2>
<h3 id="heading-order-book">Order book</h3>
<p>The order book has two sides: the buy side and the sell side.</p>
<p>Both sides contain information about the number of orders opened at each price level.</p>
<p>Each level displays:</p>
<ul>
<li><p><strong>Price</strong>: this is what defines the level. As orders must be placed at a price that is a multiple of the selected market's tick size (0.5), each level will be an increment of 0.5 (as long as there is an order open at that level).</p>
</li>
<li><p><strong>Size</strong>: the total quantity of contracts derived from open orders that have been placed at this level.</p>
</li>
<li><p><strong>Total</strong>: the summed amount of contracts derived from open orders that reside in the book at this level and above. To calculate the total of a given level we take the size of the current level and sum the sizes leading to this price level in the order book. The total is also used to calculate the depth visualizer (colored bars behind the levels). The depth of each level is calculated by taking that level's total as a percentage of the highest total in the book.</p>
</li>
</ul>
<h3 id="heading-grouping-select-box">Grouping Select Box</h3>
<p>By default the orders are grouped by the selected market's ticket size (0.5).</p>
<p>Possible toggling of the grouping is between 0.5, 1, 2.5 for XBTUSD market and 0.05, 0.1 and 0.25 for ETHUSD market.</p>
<p>To group levels, we combine the levels rounded down to the nearest group size – for example, if we change our grouping from 0.5 to 1 then we would combine the data from prices 1000 and 1000.5 and display it under a single level in the order book with the price 1000.</p>
<h3 id="heading-toggle-feed-button">Toggle Feed Button</h3>
<p>This button toggles the selected market between PI_XBTUSD and PI_ETHUSD. These are the two markets we will support -&gt; Bitcoin/USD and Ethereum/USD.</p>
<p>It supports dynamic grouping logic and handles groupings for XBT (0.5, 1, 2.5) and groupings for ETH (0.05, 0.1, 0.25).</p>
<h3 id="heading-kill-feed-button">Kill Feed Button</h3>
<p>Clicking this button stops the feed.</p>
<p>Then clicking this button a second time renews the feed.</p>
<h3 id="heading-status-message">Status Message</h3>
<p>This message will show the currently selected market. It will also show a message saying the feed is killed.</p>
<h2 id="heading-tech-stack-for-our-app">Tech Stack for our App</h2>
<p>Here is a list of the main technologies we will be using:</p>
<ul>
<li><p><a target="_blank" href="https://create-react-app.dev/docs/adding-typescript/">React with TypeScript</a> (<code>yarn create react-app my-app --template typescript</code>) — a UI library we will use for building our application’s user interfaces.</p>
</li>
<li><p><a target="_blank" href="https://redux.js.org/">Redux</a> — a state management library we will use for managing our application’s state.</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API">WebSockets</a> — The <code>WebSocket</code> object provides the API for creating and managing a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API">WebSocket</a> connection to a server, as well as for sending and receiving data on the connection. We will use it to implement the logic for consuming the live feeds as well as to be able to stop and renew.</p>
</li>
<li><p><a target="_blank" href="https://www.styled-components.com/docs">styled-components</a> — a CSS-in-JS library that lets you define the CSS styles of your components using ES6 template literals. We will use it to add styles to our app and make the look and feel beautiful. It utilizes tagged template literals to style your components and removes the mapping between components and styles. This means that when you’re defining your styles, you’re actually creating a normal React component that has your styles attached to it.</p>
</li>
<li><p><a target="_blank" href="https://github.com/testing-library/react-testing-library">react-testing-library</a> — The <code>React Testing Library</code> is a very light-weight solution for testing React components. We will use it for testing the UI components of our app.</p>
</li>
<li><p><a target="_blank" href="https://jestjs.io/">Jest</a> - a JavaScript Testing Framework that has become the de facto standard when we talk about testing React applications. We will use it to write some unit tests that will cover the reducer functions we have in our app.</p>
</li>
</ul>
<h2 id="heading-how-to-build-the-app">How to Build the App</h2>
<p>From this point onward I will try to guide you through the process I followed when building this.</p>
<p>💡 I must say that what I am showing you here is just <strong>a way</strong> of creating such an app – but it's not <strong>the way</strong> in any regard. Probably folks with more experience in crypto would do it better.</p>
<h3 id="heading-project-structure">Project Structure</h3>
<p>The project structure is pretty straightforward. We are using React and styled-components, which makes this way of structuring very convenient.</p>
<p>Let's see first what it looks like and then I will explain the what and the why.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/10/image-31.png" alt="Project structure" width="600" height="400" loading="lazy"></p>
<p><em>Project structure</em></p>
<p>As you can see on the image above, I have organized most of the components in folders. Each folder contains an <code>index.tsx</code> file, a <code>styles.tsx</code> and a <code>.test.tsx</code> files.</p>
<p><strong>index.tsx</strong> – contains the code responsible for the component logic.</p>
<p><strong>styles.tsx</strong> – contains the code responsible for styling the component. Here is where styled-components shines.</p>
<p><strong>.text.tsx</strong> – these contain the component unit tests.</p>
<p>Let me give you a short summary of the idea behind each of the components in the <code>components</code> folder. Starting top to bottom:</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Button">Button</a> renders a button with a given background color and title. It's used for the two buttons in the footer, <code>Toggle Feed</code> and <code>Kill Feed / Renew Feed</code>.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/DepthVisualizer">DepthVisualizer</a> is the component responsible for drawing the red and the green backgrounds you are seeing behind the numbers. It does this by rendering a row (an HTML <code>div</code> element) with given width, position being left (Bids) or right (Asks).</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Footer">Footer</a> – well, there's not much to say here, it contains the two buttons used in the app.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/GroupingSelectBox">GroupingSelectBox</a> renders the select box we use to change the grouping value, using setGrouping reducer to amend the application state when grouping is being changed.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Header">Header</a> renders the title of the application as well as the GroupingSelectBox component.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Loader">Loader</a> renders loading animation implemented by leveraging <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/SVG">SVG</a>.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/OrderBook">Order Book</a> contains the core logic of the app. Separated components are located in sub-folders, and the Redux state management logic is here also.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Spread">Spread</a> renders the spread value, displayed in the middle of the header (in desktop view). The component itself contains short methods for calculating the amount itself and the percentage value.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/StatusMessage">StatusMessage</a> is a small component used to display <em>status messages.</em> It basically shows which market is currently being displayed and whether the feed is killed.</p>
<h3 id="heading-rendering-performance"><strong>Rendering Performance</strong></h3>
<p>Here is a good moment to talk about <em>rendering performance</em> and <em>inline styling</em> a bit.</p>
<p><strong>Rendering</strong> is the process of React asking your components to describe what they want their section of the UI to look like based on the current combination of props and state.</p>
<p>This process is triggered by a change of the state in your component. This change could be caused by some of the props being changed or by some internal logic of the component.</p>
<p>The point here is that when re-rendering happens unnecessarily, it reduces the performance of our app. This is exactly what happened to me when I introduced the initial implementation of the <em>DepthVisualizer</em> component. It was using styled-components, that is JavaScript, for the drawing part.</p>
<p>In order to solve this, I have changed the component to use inline styles, that is pure CSS, instead of a CSS in JS approach. In other words, my bottleneck was using JavaScript animations, which is a famous reason for reduced performance.</p>
<p>Here is how it looks like now:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> DepthVisualizer: FunctionComponent&lt;DepthVisualizerProps&gt; = <span class="hljs-function">(<span class="hljs-params">{windowWidth, depth, orderType }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
    <span class="hljs-attr">backgroundColor:</span> `${<span class="hljs-attr">orderType</span> === <span class="hljs-string">OrderType.BIDS</span> ? <span class="hljs-attr">DepthVisualizerColors.BIDS</span> <span class="hljs-attr">:</span> <span class="hljs-attr">DepthVisualizerColors.ASKS</span>}`,
    <span class="hljs-attr">height:</span> "<span class="hljs-attr">1.250em</span>",
    <span class="hljs-attr">width:</span> `${<span class="hljs-attr">depth</span>}%`,
    <span class="hljs-attr">position:</span> "<span class="hljs-attr">relative</span>",
    <span class="hljs-attr">top:</span> <span class="hljs-attr">21</span>,
    <span class="hljs-attr">left:</span> `${<span class="hljs-attr">orderType</span> === <span class="hljs-string">OrderType.BIDS</span> &amp;&amp; <span class="hljs-attr">windowWidth</span> &gt;</span> MOBILE_WIDTH ? `${100 - depth}%` : 0}`,
    marginTop: -24,
    zIndex: 1,
  }} /&gt;</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> DepthVisualizer;
</code></pre>
<p><em>Inline styling</em> is when you write your CSS along with your markup, as values for the <code>style</code> attribute. This is something that is NOT considered a good practice, but as you can see here, there are cases when it's necessary to use it.</p>
<p>💡 Usually you would extract your CSS code into a separate file.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Footer">Footer</a> a simple dummy component used to render the two buttons in the footer of the app.</p>
<p>Dummy components, also known as stateless or representational ones, are components that don't hold state and are usually used just to visualize data in some way. This data is being passed via the props. For example the <code>isFeedKilled</code> flag in the component above.</p>
<p>If such a component needs to execute some kind of interaction, it usually does this by accepting (again via the props, for example <code>toggleFeedCallback</code>) callback functions that can be executed when that interaction happens. For example clicking a button.</p>
<p>On the opposite side we could have smart or state-full components. They are the ones that are connected to the app state and can manipulate it directly. Usually they are the ones that read the data from the state and pass it to the stateless components via their props.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/GroupingSelectBox">GroupingSelectBox</a> contains the Select element you can use to switch between the groupings.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/Header/index.tsx">Header</a> is the header part of the app. It takes care of setting properly the layout consisting of the title 'Order Book' on the left and the select box on the right.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/Loader">Loader</a> is used as an indicator for when the data has not yet been loaded. It leverages a SVG animation I have found online.</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/tree/main/src/components/OrderBook">Order Book</a> is where the real thing is happening. This one consists of a few smaller components:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/d8db0239763dce32fbcae499a6b7deefed9f684f/src/components/OrderBook/styles.tsx#L21">TableContainer</a> – used for styling the views for both the Odds and Bets sides.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/TitleRow/index.tsx">TitleRow</a> – this is the component responsible for displaying the titles of the columns: prize, size, and total, respectively.</p>
</li>
</ul>
<h3 id="heading-how-to-build-the-ui-with-react-and-styled-components">How to Build the UI with React and styled-components</h3>
<p>When we talk about component-based structure, such as the one <a target="_blank" href="https://reactjs.org/">React</a> provides us, the <a target="_blank" href="https://styled-components.com/">styled-components library</a> is likely one of the first choices you might make when styling is needed.</p>
<p>Like <a target="_blank" href="https://www.joshwcomeau.com/">Josh Comeau</a> says in his detailed <a target="_blank" href="https://www.joshwcomeau.com/css/styled-components/">article</a>:</p>
<blockquote>
<p>💡 It's a wonderful tool. In many ways, it's changed how I think about CSS architecture, and has helped me keep my codebase clean and modular, just like React!</p>
</blockquote>
<p>As the name of the lib hints, we could easily style our components by using the <a target="_blank" href="https://reactjs.org/docs/faq-styling.html#what-is-css-in-js">CSS-in-JS pattern</a>. Here is an example of how I used it to write the styles for my <code>Button</code> component:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">"styled-components"</span>;

interface ContainerProps {
  <span class="hljs-attr">backgroundColor</span>: string;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Container = styled.button&lt;ContainerProps&gt;<span class="hljs-string">`
  padding: .3em .7em;
  margin: 1em;
  border-radius: 4px;
  border: none;
  color: white;
  background: <span class="hljs-subst">${props =&gt; props.backgroundColor}</span>;
  font-family: "Calibri", sans-serif;
  font-size: 1.2em;

  &amp;:hover {
    cursor: pointer;
    opacity: .8;
  }
`</span>
</code></pre>
<p>Notice how I am using an <code>interface</code> in my styles file, and also the <code>background</code> property being passed as an argument via <code>props</code>. This is part of the CSS-in-JS story.</p>
<p>The possibility to use CSS code in JavaScript or (as someone might say) vice versa comes very handy. For example, when we need a component to look differently depending on something, we can pass through its props a parameter to define this.</p>
<p>As every style is actually a component, this way of writing styles feels a lot like writing React components. I mean, in the end, everything is components, right?</p>
<h3 id="heading-responsiveness-and-page-visibility-detection">Responsiveness and Page Visibility Detection</h3>
<p>While working on this app, I read in several places that, for applications which support rapid updates, is a good practice to implement some kind of mechanism for pausing the whole thing when it is not being used by the user. For example when the user minimizes the browser window or just opens another tab.</p>
<p>Since our Order book is consuming a lot of new chunks of data every second via WSS, I decided to implement such a mechanism as well.</p>
<p>What this does is:</p>
<ul>
<li><p>it shows a loader when the data is not there yet</p>
</li>
<li><p>it changes the meta title to signify that the app is in <code>paused</code> mode</p>
</li>
<li><p>it unpauses the work once the app window is on focus</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-114.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Active mode</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-115.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Paused mode</em></p>
<p>You may see the whole implementation <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/App.tsx">here</a>.</p>
<p>The essential part is in the useEffect hook, which is triggered only once when the application renders for first time.</p>
<p>In there we take advantage of the Page Visibility API by attaching the necessary listeners. And then, in the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/App.tsx#L61">handlers</a>, we simply execute the logic we want.</p>
<h3 id="heading-window-size-detection">Window Size Detection</h3>
<p>In almost every app that has some level of responsiveness, you need some logic for detecting the changes in the window size and taking some actions accordingly.</p>
<p>In other words, you need to know when your app is being viewed in certain screen size, so you can arrange your components and adjust your styles so that everything looks nice and in place.</p>
<p>This is especially valid for mobile friendly applications, where responsiveness is essential.</p>
<p>Our implementation of the window size change detection is based on the <code>innerWidtgh</code> property of the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth">browser window object</a> and <code>onresize</code> event that is being triggered when it gets resized.</p>
<p>I am attaching a listener for this event in a <code>useEffect</code> hook in <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/bd24e610e9fc4e271a6820a297b78decf4950fd9/src/App.tsx#L32">App.tsx file</a>. And then, every time the window size changes, I am setting the new width to a state variable via <code>setWindowWidth</code> hook.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> [windowWidth, setWindowWidth] = useState(<span class="hljs-number">0</span>);
...
...

<span class="hljs-comment">// Window width detection</span>
useEffect(<span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">window</span>.onresize = <span class="hljs-function">() =&gt;</span> {
    setWindowWidth(<span class="hljs-built_in">window</span>.innerWidth);
  }
  setWindowWidth(<span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">window</span>.innerWidth);
}, []);
</code></pre>
<p>Then propagate this variable down through all interested components and use it accordingly. For example here is how I use it in <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/index.tsx">Order Book/index.tsx</a> in order to know when and where to render the TitleRow component.</p>
<pre><code class="lang-jsx">{windowWidth &gt; MOBILE_WIDTH &amp;&amp; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">TitleRow</span> <span class="hljs-attr">windowWidth</span>=<span class="hljs-string">{windowWidth}</span> <span class="hljs-attr">reversedFieldsOrder</span>=<span class="hljs-string">{false}</span> /&gt;</span></span>}
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-142.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TitleRow component - desktop view</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-143.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>TitleRow component - mobile view</em></p>
<p>Note that it appears on different position depending on that whether you are seeing the app on desktop or mobile.</p>
<p>You may take a look at the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/TitleRow/index.tsx">component</a> itself and see similar approach of using the window width there.</p>
<h3 id="heading-state-management-with-redux">State Management with Redux</h3>
<p>As you probably guessed already, I used <a target="_blank" href="https://redux.js.org/">Redux</a> for managing the state of the app.</p>
<p>The main logic behind that is concentrated in the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/orderbookSlice.ts">orderbookSlice</a> reducer. In the following few lines I will walk you through it and see how and why I built it that way.</p>
<p>First we define the interface and the initial state of our order book data. The initial state contains the default values we need to have in place when starting the app.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> interface OrderbookState {
  <span class="hljs-attr">market</span>: string;
  rawBids: number[][];
  bids: number[][];
  maxTotalBids: number;
  rawAsks: number[][];
  asks: number[][];
  maxTotalAsks: number;
  groupingSize: number;
}

<span class="hljs-keyword">const</span> initialState: OrderbookState = {
  <span class="hljs-attr">market</span>: <span class="hljs-string">'PI_XBTUSD'</span>, <span class="hljs-comment">// PI_ETHUSD</span>
  <span class="hljs-attr">rawBids</span>: [],
  <span class="hljs-attr">bids</span>: [],
  <span class="hljs-attr">maxTotalBids</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">rawAsks</span>: [],
  <span class="hljs-attr">asks</span>: [],
  <span class="hljs-attr">maxTotalAsks</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">groupingSize</span>: <span class="hljs-number">0.5</span>
};
</code></pre>
<p>Then there are a few short, self-explanatory methods helping to manipulate the levels data:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> removePriceLevel = (price: number, <span class="hljs-attr">levels</span>: number[][]): number[][] =&gt; levels.filter(<span class="hljs-function"><span class="hljs-params">level</span> =&gt;</span> level[<span class="hljs-number">0</span>] !== price);

<span class="hljs-keyword">const</span> updatePriceLevel = (updatedLevel: number[], <span class="hljs-attr">levels</span>: number[][]): number[][] =&gt; {
  <span class="hljs-keyword">return</span> levels.map(<span class="hljs-function"><span class="hljs-params">level</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (level[<span class="hljs-number">0</span>] === updatedLevel[<span class="hljs-number">0</span>]) {
      level = updatedLevel;
    }
    <span class="hljs-keyword">return</span> level;
  });
};

<span class="hljs-keyword">const</span> levelExists = (deltaLevelPrice: number, <span class="hljs-attr">currentLevels</span>: number[][]): <span class="hljs-function"><span class="hljs-params">boolean</span> =&gt;</span> currentLevels.some(<span class="hljs-function"><span class="hljs-params">level</span> =&gt;</span> level[<span class="hljs-number">0</span>] === deltaLevelPrice);

<span class="hljs-keyword">const</span> addPriceLevel = (deltaLevel: number[], <span class="hljs-attr">levels</span>: number[][]): number[][] =&gt; {
  <span class="hljs-keyword">return</span> [ ...levels, deltaLevel ];
};
</code></pre>
<p>Then the real magic is happening. If the size returned by a delta is 0 then that price level should be removed from the order book. Otherwise you can safely overwrite the state of that price level with new data returned by that delta.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">/** The orders returned by the feed are in the format
 of [price, size][].
 * <span class="hljs-doctag">@param </span>currentLevels Existing price levels - `bids` or `asks`
 * <span class="hljs-doctag">@param </span>orders Update of a price level
 */</span>
<span class="hljs-keyword">const</span> applyDeltas = (currentLevels: number[][], <span class="hljs-attr">orders</span>: number[][]): number[][] =&gt; {
  <span class="hljs-keyword">let</span> updatedLevels: number[][] = currentLevels;

  orders.forEach(<span class="hljs-function">(<span class="hljs-params">deltaLevel</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> deltaLevelPrice = deltaLevel[<span class="hljs-number">0</span>];
    <span class="hljs-keyword">const</span> deltaLevelSize = deltaLevel[<span class="hljs-number">1</span>];

    <span class="hljs-comment">// If new size is zero - delete the price level</span>
    <span class="hljs-keyword">if</span> (deltaLevelSize === <span class="hljs-number">0</span> &amp;&amp; updatedLevels.length &gt; ORDERBOOK_LEVELS) {
      updatedLevels = removePriceLevel(deltaLevelPrice, updatedLevels);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-comment">// If the price level exists and the size is not zero, update it</span>
      <span class="hljs-keyword">if</span> (levelExists(deltaLevelPrice, currentLevels)) {
        updatedLevels = updatePriceLevel(deltaLevel, updatedLevels);
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// If the price level doesn't exist in the orderbook and there are less than 25 levels, add it</span>
        <span class="hljs-keyword">if</span> (updatedLevels.length &lt; ORDERBOOK_LEVELS) {
          updatedLevels = addPriceLevel(deltaLevel, updatedLevels);
        }
      }
    }
  });

  <span class="hljs-keyword">return</span> updatedLevels;
}
</code></pre>
<p>What follows after these are few helper methods. Let me say a few words about each of them now:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L82">addTotalSums</a> – with the help of this method, we iterate through the orders data, bids or asks, and calculate for each of them the total sum. The total sum value is then used for making the background visualizations.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L99">addDepths</a> – we use this method to calculate the so-called <em>depth</em> for each order. These values will be used later by the depth meter component to display the red and green rows in the background.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L113">getMaxTotalSum</a> – this one returns the max value of all total sums.</p>
</li>
</ul>
<p>Everything below is what we use for creating the application state. As per the <a target="_blank" href="https://redux-toolkit.js.org/rtk-query/overview#create-an-api-slice">Redux Toolkit documentation</a>, it’s using <code>createSlice</code>API to create the <em>slice</em>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-116.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Redux state</em></p>
<p>Creating a slice requires a string name to identify the slice, an initial state value, and one or more reducer functions to define how the state can be updated.</p>
<p>Once a slice is created, we can export the generated Redux action creators and the reducer function for the whole slice.</p>
<p>The last few lines consist of the exports in question – action creators, state slices selectors and the main reducer.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { addBids, addAsks, addExistingState, setGrouping, clearOrdersState } = orderbookSlice.actions;
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectBids = (state: RootState): number[][] =&gt; state.orderbook.bids;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectAsks = (state: RootState): number[][] =&gt; state.orderbook.asks;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectGrouping = (state: RootState): <span class="hljs-function"><span class="hljs-params">number</span> =&gt;</span> state.orderbook.groupingSize;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> selectMarket = (state: RootState): <span class="hljs-function"><span class="hljs-params">string</span> =&gt;</span> state.orderbook.market;
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> orderbookSlice.reducer;
</code></pre>
<p>With all that, our state manipulation logic is complete. 🎉</p>
<p>Now it’s time to take a look at the protocol we used in our app to take advantage of all these rapid changes in the data we consume.</p>
<h3 id="heading-websocket-protocol-wss">Websocket Protocol (WSS)</h3>
<p>As you may have noticed, we're using the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">Web Socket</a> communication protocol for fetching data into our application. We also use its features, as you will see in a moment, to accomplish other things (such as toggling the feeds and subscribe/unsubscribe from the data channel).</p>
<p><a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/index.tsx">Here</a> is how I used it.</p>
<p>Instead of trying to rely on manual implementation, I used the <a target="_blank" href="https://www.npmjs.com/package/react-use-websocket">react-use-websocket</a> package. It gives you all you need when you want to leverage WSS in a React app. If you want to go into details about this, you may take a look at their <a target="_blank" href="https://github.com/robtaussig/react-use-websocket#readme">documentation</a>.</p>
<h3 id="heading-a-few-words-about-my-implementation">A Few Words About My Implementation</h3>
<p>What we need fist is the endpoint URL where the data feeds are coming from. I am sure there are multiple options out there when we talk about cryptocurrencies. In our app I used the one provided by <a target="_blank" href="http://www.cryptofacilities.com/">www.cryptofacilities.com/</a>.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> WSS_FEED_URL: string = <span class="hljs-string">'wss://www.cryptofacilities.com/ws/v1'</span>;
</code></pre>
<p>Then the only thing we need to do to start consuming the data is to put the <code>useWebSocket</code> hook to work. As you may have guessed already, this hook is provided by the package mentioned above.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> useWebSocket <span class="hljs-keyword">from</span> [<span class="hljs-string">"react-use-websocket"</span>](<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">https:</span>//<span class="hljs-attr">github.com</span>/<span class="hljs-attr">robtaussig</span>/<span class="hljs-attr">react-use-websocket</span>&gt;</span></span>);

...
...
...

const { sendJsonMessage, getWebSocket } = useWebSocket(WSS_FEED_URL, {
    <span class="hljs-attr">onOpen</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'WebSocket connection opened.'</span>),
    <span class="hljs-attr">onClose</span>: <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'WebSocket connection closed.'</span>),
    <span class="hljs-attr">shouldReconnect</span>: <span class="hljs-function">(<span class="hljs-params">closeEvent</span>) =&gt;</span> <span class="hljs-literal">true</span>,
    <span class="hljs-attr">onMessage</span>: <span class="hljs-function">(<span class="hljs-params">event: WebSocketEventMap[<span class="hljs-string">'message'</span>]</span>) =&gt;</span>  processMessages(event)
  });
</code></pre>
<p>We pass the endpoint as the first argument and a few callback functions after that. These help us perform certain actions when one of the following happens:</p>
<ul>
<li><p><code>onOpen</code> – what to do when WebSocket connection is established.</p>
</li>
<li><p><code>onClose</code> – what to do when WebSocket connection is terminated.</p>
</li>
<li><p><code>shouldReconnect</code> – this is just a flag, saying if we want automatic reconnect when the connection drops for some reason.</p>
</li>
<li><p><code>onMessage</code> – this is the main event that brings us the chunks with the data (I call <code>processMessage</code> method every time when that happens. This means that every time when a new chunk of data is received, we process it and display it respectively).</p>
</li>
</ul>
<p>Down below is the method in question. It simply does two things:</p>
<ul>
<li><p>Either calls a method called <code>process</code> (No pun intended 😄) – this method is called every time new data for bids or asks is received and it processes it accordingly.</p>
</li>
<li><p>Dispatches an event that is using one of the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/e74dfad48990ff1a1f12ac45f5a065cc5044ee75/src/components/OrderBook/orderbookSlice.ts#L148">reducer functions</a> we have seen earlier. This function practically creates the initial state of our application.</p>
</li>
</ul>
<p>In order to decide whether we are adding data to the current state or we should initialize it, we check for a property called <code>numLevels</code>. This is something that comes from the API, the very first time we establish the WebSocket connection.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-117.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Initial payload</em></p>
<p>The rest of the code you see in this <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/index.tsx">file</a> is mostly for preparing and rendering the results on the screen.</p>
<p>The most interesting part would be the method <code>buildPriceLevels</code> that is used for both halves – bids and asks. It sorts the data, makes the necessary calculations, and passes it to the relevant components for visualizing it. Those are <code>DepthVisualizer</code> and <code>PriceLevelRow</code> I mentioned earlier in this article.</p>
<h2 id="heading-grouping">Grouping</h2>
<p>The grouping is an important part of how the order book works, as it defines by what ticket size the orders are grouped.</p>
<p>In our application, I have implemented a toggling functionality per each market, that allows grouping it as follows:</p>
<ul>
<li>Between 0.5, 1, 2.5 for XBTUSD market.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-118.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>XBTUSD market grouping</em></p>
<ul>
<li>Between 0.05, 0.1 and 0.25 for ETHUSD market.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-119.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>ETHUSD market grouping</em></p>
<p>There is a short gist I created when trying to figure out how to implement the grouping logic. You may find it <a target="_blank" href="https://gist.github.com/mihailgaberov/5faa2c1c3e4fd3e0593ad68861b989ce">here</a>.</p>
<p>Also, aside from that gist, while developing this I have performed more than a few experiments out of the project itself. And just because these are just local files on my computer, I will publish them here for those of you who are even more curious.</p>
<p>It’s a small side npm project that has only one dependency. Here is the package.json file:</p>
<pre><code class="lang-jsx">{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"grouping"</span>,
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-string">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-string">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"lodash.groupby"</span>: <span class="hljs-string">"^4.6.0"</span>
  }
}
</code></pre>
<p>And here is the code itself:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> bids = [
    [
        <span class="hljs-number">50163</span>,
        <span class="hljs-number">110</span>
    ],
    [
        <span class="hljs-number">50162</span>,
        <span class="hljs-number">13140</span>
    ],
    [
        <span class="hljs-number">50158</span>,
        <span class="hljs-number">3763</span>
    ],
    [
        <span class="hljs-number">50156</span>,
        <span class="hljs-number">1570</span>
    ],
    [
        <span class="hljs-number">50155</span>,
        <span class="hljs-number">21997</span>
    ],
    [
        <span class="hljs-number">50152.5</span>,
        <span class="hljs-number">450</span>
    ],
    [
        <span class="hljs-number">50151</span>,
        <span class="hljs-number">4669</span>
    ],
    [
        <span class="hljs-number">50150.5</span>,
        <span class="hljs-number">10329</span>
    ],
    [
        <span class="hljs-number">50150</span>,
        <span class="hljs-number">2500</span>
    ],
    [
        <span class="hljs-number">50149.5</span>,
        <span class="hljs-number">450</span>
    ],
    [
        <span class="hljs-number">50149</span>,
        <span class="hljs-number">4022</span>
    ],
    [
        <span class="hljs-number">50148</span>,
        <span class="hljs-number">20000</span>
    ],
    [
        <span class="hljs-number">50147</span>,
        <span class="hljs-number">5166</span>
    ],
    [
        <span class="hljs-number">50146.5</span>,
        <span class="hljs-number">5274</span>
    ],
    [
        <span class="hljs-number">50145</span>,
        <span class="hljs-number">174609</span>
    ],
    [
        <span class="hljs-number">50143</span>,
        <span class="hljs-number">20000</span>
    ],
    [
        <span class="hljs-number">50141</span>,
        <span class="hljs-number">28000</span>
    ],
    [
        <span class="hljs-number">50140.5</span>,
        <span class="hljs-number">5000</span>
    ],
    [
        <span class="hljs-number">50138</span>,
        <span class="hljs-number">6000</span>
    ],
    [
        <span class="hljs-number">50132.5</span>,
        <span class="hljs-number">4529</span>
    ],
    [
        <span class="hljs-number">50132</span>,
        <span class="hljs-number">4755</span>
    ],
    [
        <span class="hljs-number">50131</span>,
        <span class="hljs-number">12483</span>
    ],
    [
        <span class="hljs-number">50128.5</span>,
        <span class="hljs-number">61115</span>
    ],
    [
        <span class="hljs-number">50128</span>,
        <span class="hljs-number">23064</span>
    ],
    [
        <span class="hljs-number">50125.5</span>,
        <span class="hljs-number">181363</span>
    ]
]

<span class="hljs-comment">/* function roundDownNearest(num, acc) {
    if (acc &lt; 0) {
        return Math.floor(num * acc) / acc;
    } else {
        return Math.floor(num / acc) * acc;
    }
} */</span>

<span class="hljs-comment">/* function groupByTicketSize(ticketSize, levels) {
    const result = levels.map((element, idx) =&gt; {
        const nextLevel = levels[idx + 1];

        if (nextLevel) {
            const currentPrice = element[0];
            const currentSize = element[1];
            const nextPrice = nextLevel[0];
            const nextSize = nextLevel[1];
            console.log("current level: ", element)
            console.log("next level: ", nextLevel)

            element[0] = roundDownNearest(currentPrice, ticketSize);

            if (currentPrice - nextPrice &lt; ticketSize) {
                element[1] = currentSize + nextSize;
            }
            console.log("==================================&gt; Result: ", element)

            return element;
        }

    }).filter(Boolean); 


    console.log("============================================================");
    console.log(result)
} */</span>

<span class="hljs-keyword">const</span> test = [
    [<span class="hljs-number">1004.5</span>, <span class="hljs-number">1</span>],
    [<span class="hljs-number">1001.5</span>, <span class="hljs-number">1</span>],
    [<span class="hljs-number">1001</span>,   <span class="hljs-number">1</span>],
    [<span class="hljs-number">1000.5</span>, <span class="hljs-number">1</span>],
    [<span class="hljs-number">1000</span>,   <span class="hljs-number">1</span>],
    [<span class="hljs-number">999.5</span>,  <span class="hljs-number">1</span>],
    [<span class="hljs-number">999</span>,    <span class="hljs-number">1</span>],
    [<span class="hljs-number">990</span>,    <span class="hljs-number">1</span>],
    [<span class="hljs-number">988</span>,    <span class="hljs-number">1</span>]
]

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">groupByTicketSize</span>(<span class="hljs-params">ticketSize, levels</span>) </span>{
    <span class="hljs-keyword">const</span> result = [];

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; levels.length; i++) {
        <span class="hljs-built_in">console</span>.log(levels[i])
        <span class="hljs-keyword">const</span> prevLevel = levels[i<span class="hljs-number">-1</span>]
        <span class="hljs-keyword">const</span> level1 = levels[i]
        <span class="hljs-keyword">const</span> level2 = levels[i+<span class="hljs-number">1</span>]

        <span class="hljs-keyword">if</span> (prevLevel &amp;&amp; level1 &amp;&amp; level1[<span class="hljs-number">0</span>] - ticketSize === prevLevel) <span class="hljs-keyword">return</span>

        <span class="hljs-keyword">if</span> (level2 &amp;&amp; level1[<span class="hljs-number">0</span>] - level2[<span class="hljs-number">0</span>] &lt; ticketSize) {
            <span class="hljs-keyword">const</span> newLevel = [level2[<span class="hljs-number">0</span>], level1[<span class="hljs-number">1</span>] + level2[<span class="hljs-number">1</span>]];
            <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"newLevel"</span>, newLevel)
            result.push(newLevel);
        } <span class="hljs-keyword">else</span> {
            result.push(level1)
        }
    }

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"============================================================"</span>);
    <span class="hljs-built_in">console</span>.log(result)
}

<span class="hljs-comment">// groupByTicketSize(1, bids);</span>
groupByTicketSize(<span class="hljs-number">1</span>, test);
</code></pre>
<h2 id="heading-how-to-perform-unit-tests-on-the-app">How to Perform Unit Tests on the App</h2>
<p>For performing unit testing I used the <a target="_blank" href="https://testing-library.com/docs/react-testing-library/intro/">react-testing-library</a>.</p>
<p>The main idea behind it that the developer should write tests only for what the user will see and interact with. There is no much point of testing implementation details.</p>
<p>💡 Imagine, just to give you an example, that you have implemented a list component that is just displaying lines of textual data. Say something like a todo list.</p>
<p>Then imagine that this data is coming from an API call in the shape of array. A data structure that you could easily iterate through via various methods – some sort of a loop cycle, such as for() or while(). Or you could use another more functional approach, say .map() method.</p>
<p>Now ask yourself – for the end user, the one that will just see the listed text data, does your implementation matter? As long as everything works as expected and in a good, performant way, the answer is ‘no, it does not’.</p>
<p>This is what your tests should reflect.</p>
<p>In the context of our Order Book application, each test file is located in the same directory as the implementation file. Most of the tests are short and self-explanatory, due to the fact that these are testing mostly rendering logic and only the <a target="_blank" href="https://en.wikipedia.org/wiki/Happy_path">happy path</a>.</p>
<p>For example let’s take a look at the button component tests below:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { render, screen } <span class="hljs-keyword">from</span> <span class="hljs-string">'@testing-library/react'</span>;
<span class="hljs-keyword">import</span> Button <span class="hljs-keyword">from</span> <span class="hljs-string">'./index'</span>;

test(<span class="hljs-string">'renders button with title'</span>, <span class="hljs-function">() =&gt;</span> {
  render(<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">backgroundColor</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">red</span>'} <span class="hljs-attr">callback</span>=<span class="hljs-string">{jest.fn}</span> <span class="hljs-attr">title</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Toggle</span>'} /&gt;</span></span>);
  <span class="hljs-keyword">const</span> btnElement = screen.getByText(<span class="hljs-regexp">/Toggle/i</span>);
  expect(btnElement).toBeInTheDocument();
});
</code></pre>
<p>It just verifies that the component is rendered properly and it displays what we expect the user to see. Which is the title <em>Toggle</em> in this case.</p>
<p>For testing the <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/src/components/OrderBook/orderbookSlice.test.ts">reducers</a> I have used <a target="_blank" href="https://jestjs.io/">Jest</a>, as this is the only not visual part that we'll cover. These tests are also pretty simple and self-explanatory. I use them for testing whether the initial application state is in place and to see that adding price levels to that state works correctly.</p>
<h2 id="heading-how-to-deploy-the-app-to-vercel">How to Deploy the App to Vercel</h2>
<p>Finally – deployment time. 🎉</p>
<p>After finishing the development and testing our application, let’s put it live.</p>
<p>I used the <a target="_blank" href="https://vercel.com/">Vercel</a> platform for this purpose. They offer a pretty rich and easy to use interface as well as integrations for all famous source control platforms out there – including, of course, GitHub (where our application repo lives).</p>
<p>Assuming you have a GitHub account, what you need to do if you want to deploy it on your own is to login with it <a target="_blank" href="https://vercel.com/login">here</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-120.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel login screen</em></p>
<p>Click on the <em>+New Project</em> button in the top right corner. Then import your Git repository using the provided options in the screen that opens. Here is how mine looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-121.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel Import Git Repository screen</em></p>
<p>After importing the project, you will be able to do the actual deploy. When finished, Vercel will generate URLs for you to access your newly deployed app.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-122.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel production deployment screen</em></p>
<p>And I think you will receive an email letting you know if your deployment was successful. That email also contains these URLs.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-123.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vercel successful deployment email</em></p>
<p>Congratulations! 👏🏻</p>
<p>You now have your own <a target="_blank" href="https://orderbook-mihailgaberov.vercel.app/">Order Book application</a> up and running online.</p>
<h2 id="heading-how-to-add-a-build-badge-on-github">How to Add a Build Badge on GitHub</h2>
<p>This is not order book related, but I decided to share it with you here anyway. It’s about those small details that make the big picture somehow more complete and attractive.</p>
<p>Maybe some of you have wondered how can you get one of these so called <em>badges</em>?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-124.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Here is the answer: <a target="_blank" href="https://shields.io/">https://shields.io/</a>.</p>
<p>You go to the <a target="_blank" href="https://shields.io/category/other">Other section</a> and find the GitHub Deployments option.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-125.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then click on it and follow the instructions.</p>
<p>There is one more thing you need to do in order to have this fully functioning. You go to your GitHub repository → <a target="_blank" href="https://github.com/mihailgaberov/orderbook/actions">Actions</a> tab and create new workflow file. You may just go ahead and copy the content of <a target="_blank" href="https://github.com/mihailgaberov/orderbook/actions/runs/2143399541/workflow">mine from here</a>. Name it <em>main.yml</em>.</p>
<p>What this will do is run the jobs defined in that file. In our case this is just the build job which is basically spinning a new build and running the tests.</p>
<p>After completing this, you just need add the following lines to your <a target="_blank" href="https://github.com/mihailgaberov/orderbook/blob/main/README.md">README</a> file:</p>
<pre><code class="lang-markdown"><span class="xml"><span class="hljs-comment">&lt;!-- prettier-ignore-start --&gt;</span></span>
[<span class="hljs-string">![Tests</span>](<span class="hljs-link">&lt;https://github.com/mihailgaberov/orderbook/actions/workflows/main.yml/badge.svg&gt;</span>)](<span class="hljs-link">&lt;https://github.com/mihailgaberov/orderbook/actions/workflows/main.yml&gt;</span>)
[<span class="hljs-string">![Build Status</span>][<span class="hljs-symbol">build-badge</span>]][<span class="hljs-symbol">build</span>]

[<span class="hljs-symbol">build-badge</span>]: <span class="hljs-link">&lt;https://img.shields.io/github/deployments/mihailgaberov/orderbook/production?label=vercel&amp;logoColor=vercel&gt;</span>
[<span class="hljs-symbol">build</span>]: <span class="hljs-link">&lt;https://github.com/mihailgaberov/orderbook/deployments&gt;</span>
<span class="xml"><span class="hljs-comment">&lt;!-- prettier-ignore-end --&gt;</span></span>
</code></pre>
<p>💡 Don’t forget to put your own details in the URLs, that is your GitHub username and the name of your repository.</p>
<p>After pushing these changes you should see the badges displayed on your README: 🥳.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-126.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>GitHub badges</em></p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>If you are reading this from the beginning, I will name you a champion. 🍾</p>
<p>It has been a long trip, but hopefully interesting and fun to walk along with me!</p>
<p>Now it’s time to summarize what we have done here and try to extract some useful insights which will help us in our future development challenges.</p>
<p>I will layout below my opinion of what was the most challenging in building this application. And I will be even more eager to find out what is yours.</p>
<h3 id="heading-rendering-performance-1">Rendering Performance</h3>
<p>This really bit me in the beginning, when I was building the UI and was trying to implement the drawing of the price level rows.</p>
<p>I mentioned earlier how I have managed to solve it and I think this is going to be something I will remember for sure.</p>
<h3 id="heading-grouping-functionality">Grouping Functionality</h3>
<p>Implementing this was also kind of challenging because there were several factors I had to take into account. Because of the market we are in and the range I had to do the calculations in.</p>
<p>It took me a while to polish it (remember the side mini project and the gist I shared in the previous sections) and I still think it could be improved even more. Try switching between the markets and the grouping values multiple times and observe the results.</p>
<h3 id="heading-space-for-improvement">Space for Improvement</h3>
<p>One thing already mentioned is for sure the grouping. Which should also improve the visualizing of the red and green parts – they (almost) always should form a not ideal triangle.</p>
<p>If we try to look at the bigger picture, this order book application can be a part of a dashboard screen filled with other widgets as well, and they all can interact between them.</p>
<p>For example, changing the grouping of the order book to reflect on changing the views in the other widgets as well – say showing a market chart like this one below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/04/image-127.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I am not even mentioning adding new markets as an <em>improvement</em>, as it’s kinda clear. But this should be taken into account when building the functionality for the current markets, as to do it in a way that will be easily extendable. So that adding a new market to the order book be a trivial and quick task to do.</p>
<p>I think that's all from me.</p>
<p>Thanks for reading! 🙏</p>
<h2 id="heading-references">References</h2>
<p>Here are few links you might find useful to read:</p>
<p><a target="_blank" href="https://www.joshwcomeau.com/css/styled-components/">The styled-components Happy Path</a></p>
<p><a target="_blank" href="https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/#what-is-rendering">Blogged Answers: A (Mostly) Complete Guide to React Rendering Behavior</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Automate Simple Tasks with Node.js ]]>
                </title>
                <description>
                    <![CDATA[ Recently, I had to travel through several countries by car ?. There were a lot of tolls to pay? and a lot of gasoline⛽ to fill. Which meant a lot of bills. I collected the receipts? along the way. And I planned to calculate, at the end of the trip, ]]>
                </description>
                <link>https://www.freecodecamp.org/news/automate-simple-tasks-with-nodejs/</link>
                <guid isPermaLink="false">66d460408812486a37369d0b</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Tue, 27 Oct 2020 18:36:59 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9c9815740569d1a4ca1807.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recently, I had to travel through several countries by car ?. There were a lot of tolls to pay? and a lot of gasoline⛽ to fill. Which meant a lot of bills.</p>
<p>I collected the receipts? along the way. And I planned to calculate, at the end of the trip, how much the whole journey cost me.</p>
<p>In the end I had a full bag of papers. Which meant that I had a lot of numbers to sum up.</p>
<p>I put them in a spreadsheet on my PC, ready to start calculating them by hand. And then, my programmer's mind started talking to me - why should I do all this manual work ?️ when I could write a short program to do it for me?</p>
<p>Don't get me wrong, I am aware there are a lot of other ways to do such calculations. But since I would like to call myself a programmer who loves to automate stuff, I wanted to do it myself. In the old school way.</p>
<p>I decided to use <a target="_blank" href="https://nodejs.org/">Node.js</a> to solve this problem, mostly because I am quite comfortable with JavaScript. And this was supposed to be a <strong>very quick solution</strong> that I came up with during a cup of coffee ☕ in the morning.</p>
<p>So, here is what I did:</p>
<p>First, I filled in all the numbers I had in a txt file, each on a new line.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-112.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Data source in a txt file</em></p>
<p>Then I wrote a small program that read the data source file, parsed the numbers on a new line as a separated value to be added, and did the summing up.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

calculate = <span class="hljs-function">() =&gt;</span> {
    fs.readFile(<span class="hljs-string">'data.txt'</span>, <span class="hljs-string">'utf8'</span>, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (err) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(err)
        }

        <span class="hljs-keyword">const</span> arr = data.split(<span class="hljs-string">'\r\n'</span>);
        <span class="hljs-keyword">const</span> result = arr
            .filter(<span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> e)
            .map(<span class="hljs-built_in">parseFloat</span>)
            .reduce(<span class="hljs-function">(<span class="hljs-params">curr, next</span>) =&gt;</span> curr + next);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'RESULT: '</span>, result);
    });
}
</code></pre>
<h2 id="heading-how-i-built-this-tool">How I built this tool</h2>
<p>I will say a few words about the implementation first. Then we'll go through a brief discussion about what <strong>other options</strong> I could have chosen.</p>
<p>This is a very short function that leverages a Node.js package, <code>fs</code>. It allows us to interact with the operating system (for example to read or write files to it). This is exactly what we need in order to be able to read our data source file.</p>
<p>The code itself follows the standard <a target="_blank" href="https://www.javatpoint.com/nodejs-callbacks">Node.js callback mechanism</a>. And inside the callback function I used a bit of a functional approach: <a target="_blank" href="https://en.wikipedia.org/wiki/Pipeline_\(software\)">Piping</a> multiple methods that get the data from the previous processing, do something on it, and then pass it to the next.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-113.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Piping methods</em></p>
<p>The first method, <code>split</code>, parses the data that is being read from the text file by using the <code>\r\n</code> separator. These <a target="_blank" href="https://en.wikipedia.org/wiki/Newline">symbols</a> are used in the programming world to specify when a new line (in a file) is coming.</p>
<p>At this stage in our program, we have our numbers that have been read and parsed from the txt file. Now we use the <code>filter</code> method. This step strips the data out of any empty values.</p>
<p>Then we go for the <code>map</code> method - this is a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map">JavaScript Array method</a> that accepts a callback function. This callback will be executed on each of the arguments of a given array.</p>
<p>In our case the data is being passed implicitly – what's coming from the <code>filter</code> method output will go as input for the <code>map</code> method. And each of the members of this input will be processed by the <code>parseFloat</code> method.</p>
<p>This is <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat">another JavaScript method</a> that parses an argument, converting it to string first if needed, and returns a floating point number. We need to perform this step to guarantee that we get a correct calculation.</p>
<p>The last step from our pipeline is the <code>reduce</code> method, a third <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce">JavaScript Array method</a> we're going to use.</p>
<p>This method has <a target="_blank" href="https://www.digitalocean.com/community/tutorials/js-finally-understand-reduce">multiple applications</a>, but in our case we are using it just to sum up the numbers in the array while iterating it.</p>
<p>The reducer callback function that this method accepts does the real work. I have extracted mine in a separated named method to improve the readability of the code.</p>
<h2 id="heading-what-we-need-to-do-vs-what-we-can-do">What we need to do vs what we can do</h2>
<p>In the last section I promised a brief discussion on what else we could use to achieve the same goal.</p>
<p><em>Now it's time to stop for a moment and think about what we need to do versus what and how we can do it.</em></p>
<p>In this specific case my goal was very simple. I had some numbers that I needed to sum up automatically.</p>
<p>This made me think – what data structure did I need to put the data in, in order to have multiple choices for easy processing? That's how I came up with an array. After all, it's one of the simplest and most-used data structures in JavaScript.</p>
<p>And from here you have several options:</p>
<ol>
<li><p>You could do as I did in my example – use JavaScript Array methods such as map, filter, and reduce in a more functional manner. Or,</p>
</li>
<li><p>You could go the old fashioned way and use regular <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Loops_and_iteration">loops</a> to iterate over the array and do the calculation. Such loops could be for-, while-, forEach or even do-while JavaScript loops. In such small programs the performance is negligible, so it's up to you what to use.</p>
</li>
</ol>
<p>Both choices would work fine. What is more important here is that <strong>you should always make your decisions based on what your end goal is.</strong></p>
<p>In this article I discussed a very short and quick to implement automation tool. It did the job I needed it to do. Given the fact I didn't have much time to invest, the first working solution was good enough.</p>
<p>But there will be cases where you should perform a much more sophisticated analysis in advance so that you end up with good quality software in the end.</p>
<blockquote>
<p>Keep your end goal as your guide when deciding what to do and how to do it, and you will always be on track.</p>
</blockquote>
<h2 id="heading-give-it-a-shot">Give it a shot</h2>
<p>If you want to try it yourself, please make sure you have installed Node.js on your system. Then go ahead and check out this <a target="_blank" href="https://github.com/mihailgaberov/calc-expenses">repository</a>.</p>
<p>In order to run the program, use the following command when you're in the directory where the calc.js file is:</p>
<pre><code class="lang-bash">node calc.js
</code></pre>
<p>Check your console window to see the result. Mine looks like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/10/image-114.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Console output</em></p>
<p>That's all what I wanted to share with you. I hope part of this experience will stay with you for your future automation tasks.</p>
<p>? Thanks for reading! ?</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an SPA with Vue.js and C# Using .NET Core ]]>
                </title>
                <description>
                    <![CDATA[ Let's say you are a front-end developer. Or you have just had to work more with the front end recently. Now and then you have used some back-end technologies, but you've always stayed in your comfort zone, perhaps in the JavaScript world. Or maybe yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-spa-with-vuejs-and-c-using-net-core/</link>
                <guid isPermaLink="false">66d4604a8812486a37369d15</guid>
                
                    <category>
                        <![CDATA[ C ]]>
                    </category>
                
                    <category>
                        <![CDATA[ C# ]]>
                    </category>
                
                    <category>
                        <![CDATA[  Single Page Applications  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Vue.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Tue, 15 Sep 2020 19:14:12 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/09/dashboard-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Let's say you are a front-end developer. Or you have just had to work more with the front end recently.</p>
<p>Now and then you have used some back-end technologies, but you've always stayed in your comfort zone, perhaps in the JavaScript world. Or maybe you have built a small API with Python.</p>
<p>But you have never touched the modern .NET family tech stack.</p>
<p>This tutorial will guide you, step-by-step, in building a modern single page application (SPA) that will take advantage of <a target="_blank" href="https://vuejs.org/">Vue.js</a> for the front-end and <a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/core/get-started?tabs=windows">.NET Core (C#)</a> for the back-end.</p>
<p>We will also see how to write some tests, both unit and integration, to cover the front and back end functionality (at least partially).</p>
<p>If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/pizza-app">here</a> ? is the GitHub repository with a detailed <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/README.md">README</a> ?.</p>
<h2 id="heading-what-will-we-build">What Will We Build</h2>
<p>?‍? We will build a web app where users can signup/login and just tell us how much they love pizzas, by pressing an "I love it" button.</p>
<p>There are no restrictions on the number of times each user can show us their appreciation. The only requirement is that only logged in users can vote.</p>
<p>On the home page, along with the signup/login buttons, we will put a little bar-chart, displaying the top X users with the highest appreciation (on the X-axis) and the number of votes on the Y-axis.</p>
<h2 id="heading-installation">Installation</h2>
<p>Let's start with the front end. It makes more sense to build the visible parts of our app first. We will build our front end with one of the most famous front-end libraries on the market: Vue.js.</p>
<h3 id="heading-how-to-set-up-the-front-end">How to Set Up the Front End</h3>
<p>In order to install and configure our initial front end setup with Vue.js, we will use the <a target="_blank" href="https://cli.vuejs.org/">Vue CLI</a>. This is a standard command-line tool for developing Vue.js applications.</p>
<p>To install it, use the following command. Note that all commands in this tutorial use <a target="_blank" href="https://www.npmjs.com/">NPM</a>, which you need to have installed on your machine in order to follow along.</p>
<pre><code class="lang-bash">npm install -g @vue/cli
</code></pre>
<p>After successfully installing the Vue CLI, we should be able to start using it to install and configure Vue.js itself. Let's do with the following process.</p>
<p>Step 1: Create a new empty project directory and open it with the following commands:</p>
<pre><code class="lang-python">mkdir pizza-app
cd pizza-app
</code></pre>
<p>Step 2: While in the project root, run the following:</p>
<pre><code class="lang-bash">vue create frontend
</code></pre>
<p>Then from the provided options, select the following:</p>
<ul>
<li>Manual select features</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-52.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js installation - Manually select features</em></p>
<ul>
<li><p>Babel</p>
</li>
<li><p>Router</p>
</li>
<li><p>CSS-Preprocessors (SASS)</p>
</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-53.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js installation - select features</em></p>
<p>Then select version 2.x from the next screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-54.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Select version 2.x</em></p>
<p>Next, select 'Use history mode for router?' and 'Sass/SCSS (with node-sass)', like so:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Linter / Formatter</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Unit Testing with Jest</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>E2E Testing with Cypress</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-60.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After this last step, finish the installation process with the default options.</p>
<p>Now we are ready to run the app by using the following commands from the root project folder:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
npm run serve
</code></pre>
<p>After the app is running, you can see it in your browser on http://localhost:8080:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/welcome-to-your-vuejs-app.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js default installation screen</em></p>
<p>Before going on with building the actual components that our front-end app will have, let's install our back-end app via the .NET Core CLI.</p>
<h3 id="heading-how-to-set-up-the-back-end">How to Set Up the Back End</h3>
<p>As mentioned above, we will use another command line tool, the .NET Core CLI, that gives us the ability to create and configure .NET applications.</p>
<p>If you don't have it already, you may use <a target="_blank" href="https://dotnet.microsoft.com/download/dotnet-core/thank-you/sdk-3.1.401-windows-x64-installer">this link</a> to download it and then install it.</p>
<p>Once you have the .NET Core CLI tool installed, go to your project's root directory and run the following command to create our back-end app. Thus we will create a folder called <code>backend</code> and install a .NET web app inside it:</p>
<pre><code class="lang-python">dotnet new web -o backend
</code></pre>
<h3 id="heading-gitignoreio">gitignore.io</h3>
<p>Before continuing with installing the next packages we will need, let's sort out our <em>.gitignore</em> file.</p>
<p>This is a configuration file that tells <a target="_blank" href="https://git-scm.com/">Git</a> what to ignore when committing changes to Git based repositories (the ones in <a target="_blank" href="https://github.com/">GitHub</a>).</p>
<p>Since we want to have one .<em>gitignore</em> file, it will include rules for two types of applications:</p>
<ul>
<li><p>a Node.js based one, which is our Vue.js front end, and</p>
</li>
<li><p>a .NET (C#) one which is our back end.</p>
</li>
</ul>
<p>For this, we will use a tool called <a target="_blank" href="https://www.toptal.com/developers/gitignore"><em>gitignore.io</em></a><em>.</em> This tool will generate such files for us. The advantage of using this tool is that it allows us to type in what our programming languages/platforms are, and it generates the .gitignore file for us.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-61.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Use gitignore.io to generate a .gitignore file</em></p>
<p>Also, at the top of the generated file, it saves links for creation or subsequent editing, as shown below.</p>
<pre><code class="lang-python">
<span class="hljs-comment"># Created by https://www.toptal.com/developers/gitignore/api/webstorm,vue,vuejs,visualstudio</span>
<span class="hljs-comment"># Edit at https://www.toptal.com/developers/gitignore?templates=webstorm,vue,vuejs,visualstudio</span>
</code></pre>
<p>Now we are good to go with the rest of the packages we need to install.</p>
<p>First we will install a package called SpaServices, which will allow us to have our app running via only one URL, and pointing to the .NET app. From its side it will proxy requests to our front-end app.</p>
<p>To install it, open your terminal, go to the <code>backend</code> folder of your project, and run the following:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.SpaServices.Extensions --version 3.1.8
</code></pre>
<p>After this, we need to configure our back-end app with the SpaServices package in order to have the desired result.</p>
<p>Every request will go to our .NET back-end app, and if the request is meant for the front end, it will proxy it.</p>
<p>To do this, open the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/Startup.cs">Startup.cs</a> file and update its content to be like this:</p>
<pre><code class="lang-c#"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Linq;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Builder;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Hosting;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Http;
<span class="hljs-keyword">using</span> Microsoft.Extensions.DependencyInjection;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Hosting;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Authentication.JwtBearer;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;
<span class="hljs-keyword">using</span> Microsoft.Extensions.Configuration;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">backend</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Startup</span>
    {
        <span class="hljs-keyword">public</span> IConfiguration Configuration { <span class="hljs-keyword">get</span>; }

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Startup</span>(<span class="hljs-params">IConfiguration configuration</span>)</span>
        {
            Configuration = configuration;
        }

        <span class="hljs-comment">// This method gets called by the runtime. Use this method to add services to the container.</span>
        <span class="hljs-comment">// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>)</span>
        {
            <span class="hljs-keyword">string</span> connectionString = Configuration.GetConnectionString(<span class="hljs-string">"DefaultConnection"</span>);
            services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt; options.UseSqlite(connectionString));
            services.AddSpaStaticFiles(configuration: options =&gt; { options.RootPath = <span class="hljs-string">"wwwroot"</span>; });
            services.AddControllers();
            services.AddCors(options =&gt;
            {
                options.AddPolicy(<span class="hljs-string">"VueCorsPolicy"</span>, builder =&gt;
                {
                    builder
                    .AllowAnyHeader()
                    .AllowAnyMethod()
                    .AllowCredentials()
                    .WithOrigins(<span class="hljs-string">"https://localhost:5001"</span>);
                });
            });
            services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
  .AddJwtBearer(options =&gt;
                {
                    options.Authority = Configuration[<span class="hljs-string">"Okta:Authority"</span>];
                    options.Audience = <span class="hljs-string">"api://default"</span>;
                });
            services.AddMvc(option =&gt; option.EnableEndpointRouting = <span class="hljs-literal">false</span>);
        }

        <span class="hljs-comment">// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Configure</span>(<span class="hljs-params">IApplicationBuilder app, IWebHostEnvironment env, ApplicationDbContext dbContext</span>)</span>
        {
            <span class="hljs-keyword">if</span> (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseCors(<span class="hljs-string">"VueCorsPolicy"</span>);

            dbContext.Database.EnsureCreated();
            app.UseAuthentication();
            app.UseMvc();
            app.UseRouting();
            app.UseEndpoints(endpoints =&gt; { endpoints.MapControllers(); });
            app.UseSpaStaticFiles();
            app.UseSpa(configuration: builder =&gt;
            {
                <span class="hljs-keyword">if</span> (env.IsDevelopment())
                {
                    builder.UseProxyToSpaDevelopmentServer(<span class="hljs-string">"http://localhost:8080"</span>);
                }
            });
        }
    }
}
</code></pre>
<p>? This is the final version of the Startup.cs file and that's why you probably noticed some more stuff in it. We will get back to this a bit later in this tutorial.</p>
<p>At this point, you should be able to run your back-end app. If you wish to do so, run the following commands from the root folder of your project:</p>
<pre><code class="lang-python">cd backend
dotnet run
</code></pre>
<h2 id="heading-how-to-set-up-authentication">How to Set Up Authentication</h2>
<p>As you may remember from the description at the beginning, our app should have a Sign up/Login option.</p>
<p>In order to fulfill this requirement, we will use a 3rd party service called <a target="_blank" href="https://www.okta.com/">Okta</a>. We will install the necessary packages for using the Okta SDK on both the front end and back end of our app.</p>
<p>But before that, you need to create an account on their <a target="_blank" href="https://developer.okta.com/">website</a> and get access to their admin panel. From there you may create a new application. Here is how it looks on mine:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-62.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now let's start with the package we need for our front end. From the root folder, run the following commands:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> frontend
npm i @okta/okta-vue
</code></pre>
<p>After this step, we are ready to amend our Vue.js app in order to take advantage of Okta SDK.</p>
<p>We will also install another package, called <a target="_blank" href="https://bootstrap-vue.org/">BootstrapVue</a>, that provides a set of good looking and ready to use Vue.js components. This will help us save development time and will also get us a good looking front end.</p>
<p>To install it, run the following from your <code>frontend</code> folder:</p>
<pre><code class="lang-bash">npm i vue bootstrap-vue bootstrap
</code></pre>
<h3 id="heading-how-to-set-up-the-router">How to Set Up the Router</h3>
<p>It's time to do some coding. We need to edit our <a target="_blank" href="https://router.vuejs.org/">router</a> in order to apply what's coming from Okta authentication services.</p>
<p>You can see the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/router/index.js">full file</a> in my GitHub repo, but here is the essential part that you need to configure with your own details (that you got when you registered on the Okta developer website):</p>
<pre><code class="lang-javascript">Vue.use(Auth, {
  <span class="hljs-attr">issuer</span>: <span class="hljs-string">'https://dev-914982.okta.com/oauth2/default'</span>,
  <span class="hljs-attr">client_id</span>: <span class="hljs-string">'0oatq53f87ByM08MQ4x6'</span>,
  <span class="hljs-attr">redirect_uri</span>: <span class="hljs-string">'https://localhost:5001/implicit/callback'</span>,
  <span class="hljs-attr">scope</span>: <span class="hljs-string">'openid profile email'</span>
})

....

router.beforeEach(Vue.prototype.$auth.authRedirectGuard())
</code></pre>
<h3 id="heading-home-screen">Home screen</h3>
<p>After we get our router sorted, we can finally do some changes to our app's home screen, which will actually be visible to our users.</p>
<p>Open the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/App.vue">App.vue</a> file in your IDE and change its content as follows:</p>
<pre><code class="lang-javascript">&lt;template&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar</span> <span class="hljs-attr">toggleable</span>=<span class="hljs-string">"md"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"light"</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"light"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-toggle</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"nav-collapse"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-toggle</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-brand</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Love Pizza<span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-brand</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">b-collapse</span> <span class="hljs-attr">is-nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nav-collapse"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">b-navbar-nav</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">b-nav-item</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"login"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"!user"</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">b-nav-item</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">b-nav-item</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> @<span class="hljs-attr">click.prevent</span>=<span class="hljs-string">"logout"</span> <span class="hljs-attr">v-else</span>&gt;</span>Logout<span class="hljs-tag">&lt;/<span class="hljs-name">b-nav-item</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar-nav</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">b-collapse</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">b-navbar</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">header</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        Love pizza button and clicks counter here
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/template&gt;

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'app'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">user</span>: <span class="hljs-literal">null</span>
    }
  },
  <span class="hljs-keyword">async</span> created() {
    <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
  },
  <span class="hljs-attr">watch</span>: {
    <span class="hljs-string">'$route'</span>: <span class="hljs-string">'onRouteChange'</span>
  },
  <span class="hljs-attr">methods</span>: {
    login() {
      <span class="hljs-built_in">this</span>.$auth.loginRedirect()
    },
    <span class="hljs-keyword">async</span> onRouteChange() {
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
    },
    <span class="hljs-keyword">async</span> refreshUser() {
      <span class="hljs-built_in">this</span>.user = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.$auth.getUser()
    },
    <span class="hljs-keyword">async</span> logout() {
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.$auth.logout()
      <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.refreshUser()
      <span class="hljs-built_in">this</span>.$router.push(<span class="hljs-string">'/'</span>)
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span></span>

<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"scss"</span>&gt;</span>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#nav {
  padding: 30px;

  a {
    font-weight: bold;
    color: #2c3e50;

    &amp;.router-link-exact-active {
      color: #42b983;
    }
  }
}
<span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span></span>
</code></pre>
<p>By now your app might look something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/08/image-173.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Vue.js front-end app - work in progress</em></p>
<p><strong>Note</strong>: Don't be confused by the text 'Pizza button and clicks counter here'. When building UIs, it's a good practice to leave such placeholders for components that are to be developed in the future.</p>
<p>In our case, this is where we will add the components responsible for the 'I love it' button and the counter later. They will show the number of votes per user.</p>
<pre><code class="lang-javascript">&lt;main&gt;
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      Love pizza button and clicks counter here
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
&lt;/main&gt;
</code></pre>
<h3 id="heading-authentication-on-the-back-end">Authentication on the back end</h3>
<p>By now, we have setup our front end to leverage the authentication service provided by Okta. But in order to have the whole picture ready to use, we need to do the same in our back-end app.</p>
<p>This is where we will be doing HTTP calls from, in order to communicate with the database. And, as you will see later, some of these calls will need to be authenticated in order to succeed.</p>
<p>Let's start again with installing some packages that will make our job easier. In your terminal, go to your <code>backend</code> directory and run the following:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer --version 3.1.8
dotnet add package Microsoft.Extensions.Configuration --version 3.1.7
</code></pre>
<p>Then we need another package that will help us set up our database. We will use the SQLite database which is super easy to use in .NET Core setup.</p>
<p>While still in the <code>backend</code> folder, run:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 2.1.1
</code></pre>
<p>Once we're finished with the installations, make sure you got the full content of <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/Startup.cs">Startup.cs</a> file (and if you didn't, do so now).</p>
<h3 id="heading-pizzavotesapiservice">PizzaVotesApiService</h3>
<p>OK everyone, we have set up both our front and back ends to support authentication. We have added SQLite as a database to be used for storing the users votes. And we have an initial version of our home screen.</p>
<p>Now it's time to implement a service on the front end that will allow us to consume our back end's API.</p>
<p>Great job so far! ?</p>
<p>Before being able to make HTTP calls from our front-end app to our back-end app, we need to install one more package in our Vue.js app. It's called <a target="_blank" href="https://www.npmjs.com/package/axios">axios</a> and it gives us the ability to make <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest">XMLHttpRequests</a> from the browser, which is exactly what we need.</p>
<p>Open your terminal, go to the <code>frontend</code> of your project, and run:</p>
<pre><code class="lang-python">npm i axious
</code></pre>
<p>Then, in your IDE, go to the <code>src</code> folder of your front-end app, create a new .js file, and add the following inside it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span>
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>

const client = axios.create({
    baseURL: <span class="hljs-string">'http://localhost:5000/api/pizzavotes'</span>,
    json: true
})

export default {
    <span class="hljs-keyword">async</span> execute(method, resource, data) {
        const accessToken = <span class="hljs-keyword">await</span> Vue.prototype.$auth.getAccessToken()
        <span class="hljs-keyword">return</span> client({
            method,
            url: resource,
            data,
            headers: {
                Authorization: `Bearer ${accessToken}`
            }
        }).then(req =&gt; {
            <span class="hljs-keyword">return</span> req.data
        })
    },
    getAll() {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'get'</span>, <span class="hljs-string">'/'</span>)
    },
    getById(id) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'get'</span>, `/${id}`)
    },
    create(data) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'post'</span>, <span class="hljs-string">'/'</span>, data)
    },
    update(id, data) {
        <span class="hljs-keyword">return</span> this.execute(<span class="hljs-string">'put'</span>, `/${id}`, data)
    },
}
</code></pre>
<p>I have named mine <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/PizzaVotesApiService.js">PizzaVotesApiService.js</a>. We will stop with integrating the API for a while and will create another component that will hold the controls the users will use to interact with this API.</p>
<h3 id="heading-dashboard-component">Dashboard Component</h3>
<p>Say hello to our <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/Dashboard.vue">Dashboard.vue</a> component.</p>
<p>This is where we will put the 'I love it' button and a small votes counter.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-63.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>'I love it' button and votes counter</em></p>
<p>We'll also add a nice pizza image.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-64.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Pizza image</em></p>
<p>As well as a nice bar chart, showing the top X voters.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Top voters bar chart</em></p>
<p>You may copy and paste the full content of the file from my repo so we can continue with integrating the whole thing.</p>
<h3 id="heading-api-integration">API Integration</h3>
<p>I am going to use a small diagram to depict the data flow. As they say, "a picture is worth a thousand words:"</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/data-flow.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Data flow diagram</em></p>
<p>As you can see (I hope ?) from the diagram, when the user enters their votes, the data goes from the dashboard component through the API service we created for communicating with the back-end API. It then reaches the back-end controller that is actually making the HTTP calls.</p>
<p>Once the data is fetched, the service passes it back to our UI where we show it via our Vue.js components. As you will see, there is some additional logic that checks if the user is authenticated in order to know which calls to execute.</p>
<p>Here is the implementation of the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/PizzaVotesController.cs">controller</a> itself:</p>
<pre><code class="lang-c#"><span class="hljs-keyword">using</span> System.Collections.Generic;
<span class="hljs-keyword">using</span> System.Threading.Tasks;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Authorization;
<span class="hljs-keyword">using</span> Microsoft.AspNetCore.Mvc;
<span class="hljs-keyword">using</span> Microsoft.EntityFrameworkCore;

<span class="hljs-keyword">namespace</span> <span class="hljs-title">backend.Controllers</span>
{
    [<span class="hljs-meta">Route(<span class="hljs-meta-string">"api/[controller]"</span>)</span>]
    [<span class="hljs-meta">ApiController</span>]
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">PizzaVotesController</span> : <span class="hljs-title">ControllerBase</span>
    {
        <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> ApplicationDbContext _dbContext;

        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">PizzaVotesController</span>(<span class="hljs-params">ApplicationDbContext dbContext</span>)</span>
        {
            _dbContext = dbContext;
        }

        <span class="hljs-comment">// GET api/pizzavotes</span>
        [<span class="hljs-meta">HttpGet</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&lt;List&lt;PizzaVotes&gt;&gt;&gt; Get()
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.ToListAsync();
        }

        <span class="hljs-comment">// GET api/pizzavotes/{email}</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpGet(<span class="hljs-meta-string">"{id}"</span>)</span>]
        <span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&lt;PizzaVotes&gt;&gt; Get(<span class="hljs-keyword">string</span> id)
        {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.FindAsync(id);
        }

        <span class="hljs-comment">// POST api/pizzavotes</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpPost</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">Post</span>(<span class="hljs-params">PizzaVotes model</span>)</span>
        {
            <span class="hljs-keyword">await</span> _dbContext.AddAsync(model);
            <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();
        }

        <span class="hljs-comment">// PUT api/pizzavotes/{email}</span>
        [<span class="hljs-meta">Authorize</span>]
        [<span class="hljs-meta">HttpPut(<span class="hljs-meta-string">"{id}"</span>)</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;ActionResult&gt; <span class="hljs-title">Put</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> id, PizzaVotes model</span>)</span>
        {
            <span class="hljs-keyword">var</span> exists = <span class="hljs-keyword">await</span> _dbContext.PizzaVotes.AnyAsync(f =&gt; f.Id == id);
            <span class="hljs-keyword">if</span> (!exists)
            {
                <span class="hljs-keyword">return</span> NotFound();
            }

            _dbContext.PizzaVotes.Update(model);

            <span class="hljs-keyword">await</span> _dbContext.SaveChangesAsync();

            <span class="hljs-keyword">return</span> Ok();
        }
    }
}
</code></pre>
<p>Here we have four methods for executing four basic operations:</p>
<ul>
<li><p>get all records from the database</p>
</li>
<li><p>get the records for one user (by their email address)</p>
</li>
<li><p>create a new user record</p>
</li>
<li><p>update an existing user's records.</p>
</li>
</ul>
<p>You have probably noticed the <code>[Authorize]</code> clause above in three of the four methods. Those methods are going to require the user to be authenticated in order to execute.</p>
<p>We will leave the method <code>GET api/pizzavotes</code> for getting all records unauthorized on purpose. Since we would like to show the bar chart on the home screen to all users, we will need to be able to make this call, no matter if the user is authenticated or not.</p>
<h3 id="heading-allow-registration">Allow registration</h3>
<p>Something to note: if you would like to have a 'Sign Up' on your login screen, you need to allow it from the Okta admin panel.</p>
<p>Once logged in, select from the menu <strong>Users-&gt;Registration</strong>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-66.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Allow registration for new users</em></p>
<h3 id="heading-finish-the-back-end">Finish the back end</h3>
<p>In order for your back-end app to become fully functioning, please take a look at <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/tree/master/backend">my repo here</a> and add the missing files.</p>
<p>If you have followed along up to this point you should have the following files (except the <code>test</code> folder, as we are going to add it a bit later):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Back-end app structure</em></p>
<h3 id="heading-finish-the-front-end">Finish the front end</h3>
<p>In order to finalize the work on our front-end app, we will create two more components.</p>
<p>The first one will render the bar chart mentioned above, and the second one will display the email address of the user that is currently logged in.</p>
<p>In your IDE, go to <code>frontend/src/components</code> and create two files, named <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/Username.vue">Username.vue</a> and <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/src/components/VotesChart.vue">VotesChart.vue</a>, respectively.</p>
<p>Username.vue is a very short and simple component that takes the user's email address as a prop and renders it. Here is its implementation:</p>
<pre><code class="lang-python">&lt;template&gt;
  &lt;div <span class="hljs-class"><span class="hljs-keyword">class</span>="<span class="hljs-title">username</span>"&gt;{{ <span class="hljs-title">username</span> }}&lt;/<span class="hljs-title">div</span>&gt;
&lt;/<span class="hljs-title">template</span>&gt;

&lt;<span class="hljs-title">script</span>&gt;
<span class="hljs-title">export</span> <span class="hljs-title">default</span> {
  <span class="hljs-title">props</span>:</span> { username: String },
}
&lt;/script&gt;

&lt;style lang=<span class="hljs-string">"scss"</span>&gt;
.username {
  color: rebeccapurple;
  display: flex;
  align-items: center;
  justify-content: center;
}

.username::before {
  content: <span class="hljs-string">"•"</span>;
}

.username::after {
  content: <span class="hljs-string">"•"</span>;
}
&lt;/style&gt;
</code></pre>
<p>The only thing to notice here is that we are using SASS/SCSS for the component styles. That is possible because we chose that option in the beginning (when we were installing our Vue.js app).</p>
<p>For drawing the chart we will use a package called <a target="_blank" href="https://www.npmjs.com/package/vue-chartjs">vue-chartsjs</a>. Install it by running the following command from your <code>frontend</code> folder:</p>
<pre><code class="lang-python">npm i vue-chartjs chart.js
</code></pre>
<p>Our component <strong>VotesChart.vue</strong> is going to be kind of wrapper for the bar chart component that comes from the vue-chartjs package.</p>
<p>We use it for getting the data from the parent component, <strong>Dashboard.vue</strong>, and processing it.</p>
<p>We sort the data array in order to display our top voters, sorted from the largest to the smallest number of votes. Then we pass it to the Bar chart component to visualize it.</p>
<p>Here is the full implementation:</p>
<pre><code class="lang-python">&lt;script&gt;
<span class="hljs-keyword">import</span> { Bar } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue-chartjs'</span>

const TOP_N_VOTERS_TO_SHOW_ON_CHART = <span class="hljs-number">10</span>

// Used to sort by votes value - <span class="hljs-keyword">from</span> bigger to smaller (desc)
function sortByStartDate(nextUser, currentUser) {
  const nextVoteValue = nextUser.value
  const currentVoteValue = currentUser.value
  <span class="hljs-keyword">return</span> currentVoteValue - nextVoteValue
}

export default {
  extends: Bar,
  props: { data: Array },

  watch: {
    data: <span class="hljs-keyword">async</span> function (newVal) {
      const sortedVotes = Array.<span class="hljs-keyword">from</span>(newVal).sort(sortByStartDate).slice(<span class="hljs-number">0</span>, TOP_N_VOTERS_TO_SHOW_ON_CHART)
      this.labels = sortedVotes.map(user =&gt; user.id)
      this.values = sortedVotes.map(user =&gt; user.value)

      this.renderChart({
        labels: this.labels,
        datasets: [
          {
            label: <span class="hljs-string">'Pizza Lovers'</span>,
            backgroundColor: <span class="hljs-string">'#f87979'</span>,
            data: this.values,
          }
        ]
      }, {
        scales: {
          yAxes: [{
            ticks: {
              stepSize: <span class="hljs-number">1</span>,
              min: <span class="hljs-number">0</span>,
              max: this.values[<span class="hljs-number">0</span>]
            }
          }]
        }
      })
    }
  }
}
&lt;/script&gt;
</code></pre>
<p>Note that there is a constant at the top of the file which will define how many top voters we would like to show on this chart. Currently it is set to 10, but you can amend it as you like.</p>
<p>Once you are ready with all the front-end stuff and want to run the app, you can do so by:</p>
<p>Going to the <code>frontend</code> folder and running:</p>
<pre><code class="lang-bash">npm run serve
</code></pre>
<p>Going to the <code>backend</code> folder and running:</p>
<pre><code class="lang-bash">dotnet run
</code></pre>
<p>Opening your browser and going to https://localhost:5001.</p>
<h2 id="heading-how-to-test-our-applications">How to test our applications</h2>
<p>So far we have built a modern and fully functioning single page application with a .NET Core back end and a SQLite database. That's a lot of work. Congrats! ✨</p>
<p><em>We easily could stop here and go have some cold</em> ?.</p>
<p>But...</p>
<p>We are reasonable enough to know that if we don't test our apps, we can't guarantee that they will work as expected.</p>
<p>We also know that if we want to make our code testable, we should write in a well structured manner, keeping in mind the <a target="_blank" href="https://www.dotnettricks.com/learn/designpatterns/different-types-of-software-design-principles">main principles of software design</a>.</p>
<p>I hope I've convinced you to continue working through this tutorial. After all, the only thing left for us to do is to write some tests for both of our applications. So let's do it!</p>
<p>We will cover the functioning of our back end API with <strong>integration tests</strong>, and for our front end we will write both <strong>unit</strong> and <strong>integration</strong> tests.</p>
<h3 id="heading-unit-and-integration-tests">Unit and integration tests</h3>
<p>? Before going to the code, I would like to say a few words about these types of tests and answer some of most frequently asked questions.</p>
<p>You might be wondering, what are unit tests? What are integrations tests? Why do we need them? When should we use each of them?</p>
<p>Starting from the first question, a <strong>unit test</strong> is a piece of code that tests the functionality of an encapsulated module (meaning another piece). It's written in as a function or some kind of independent block of code.</p>
<p>They are good to have, because they give you quick development time feedback and keep the code safe from regressions when new features are being added.</p>
<p><strong>Integration tests</strong> are useful when we need to test how multiple modules/units are working together. For example a REST API and a database interaction.</p>
<p>Depending on the size of the application we are working on, we might need only integration tests. But sometimes we need both integration and unit tests to bullet proof our code.</p>
<p>Ideally, we should have them both, as they are essential parts of the testing pyramid and are the cheapest to implement.</p>
<p>But in some cases, like for our back-end app, only integration tests are necessary to cover the functionality that is worth being tested. We have only several API methods for working with the database.</p>
<h3 id="heading-how-to-create-our-back-end-tests">How to Create Our Back End Tests</h3>
<p>This time we will start with creating our test solution. To do so, you need to do a few things.</p>
<p>First, install the xUnit library by running the following from your project root directory:</p>
<pre><code class="lang-bash">dotnet add package xUnit
</code></pre>
<p>Then go to your <code>backend</code> folder and create and empty folder called <code>tests</code> . Then inside that folder run:</p>
<pre><code class="lang-bash">dotnet new xunit -o PizzaVotes.Tests
</code></pre>
<p>Once that's done, open <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/backend.csproj">backend.csproj</a> and add the following two lines to the <code>&lt;PropertyGroup&gt;</code> block:</p>
<pre><code class="lang-python">&lt;GenerateAssemblyInfo&gt;false&lt;/GenerateAssemblyInfo&gt;
&lt;GenerateTargetFrameworkAttribute&gt;false&lt;/GenerateTargetFrameworkAttribute&gt;
</code></pre>
<p>Then go to your <code>tests</code> folder and install the following packages:</p>
<pre><code class="lang-python">Microsoft.AspNetCore.Mvc
Microsoft.AspNetCore.Mvc.Core
Microsoft.AspNetCore.Diagnostics
Microsoft.AspNetCore.TestHost
Microsoft.Extensions.Configuration.Json
Microsoft.AspNetCore.Mvc.Testing
</code></pre>
<p>You do that by executing each of the following commands in your terminal app:</p>
<pre><code class="lang-bash">dotnet add package Microsoft.AspNetCore.Mvc --version 2.2.0
dotnet add package Microsoft.AspNetCore.Mvc.Core --version 2.2.5
dotnet add package Microsoft.AspNetCore.Diagnostics --version 2.2.0
dotnet add package Microsoft.AspNetCore.TestHost --version 3.1.8
dotnet add package Microsoft.AspNetCore.Mvc.Testing --version 3.1.8
</code></pre>
<p>After we have everything installed, we are ready to proceed to actually writing some tests.</p>
<p>As you may see here, except the tests themselves, I have added two more files which we need or are good to have when running the tests.</p>
<p>One of them is just a <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/ContentHelper.cs">helper file</a> with one method for dealing with serialized objects and getting the string content. The other is the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/TestFixture.cs">fixture</a> file, where we have configurations and settings for our test server and client.</p>
<p>And, of course, there's <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/backend/tests/PizzaVotes.Tests/PizzaVotesTests.cs">a file</a> with our tests.</p>
<p>I am not going to paste the content of these files here, as this tutorial has become long enough already.</p>
<p><strong>You can just copy them from my repository.</strong></p>
<p>If you take a closer look at the tests, you might notice that we are testing only the first, not authenticated, call for a success response. The rest we are checking only for 401 HTTP response, which is <code>Unautorized</code>.</p>
<p>That's because only the first method is public, that is, it doesn't need authentication.</p>
<p>If we were to have the same tests for all methods, we would have needed to implement a middleware just to authorize our test app in front of Okta's authentication services.</p>
<p>And since the purpose of this tutorial is to learn a variety of things, we might say it's not worth doing.</p>
<p>Now the fun part: how to run the tests. It turns out to be super simple. Just go to your <code>tests</code> directory (where the tests.sln file is) from your terminal and run:</p>
<pre><code class="lang-bash">dotnet <span class="hljs-built_in">test</span>
</code></pre>
<p>You should see something like this in your terminal (ignore the yellow warnings):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-68.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Running back-end tests</em></p>
<h3 id="heading-how-to-create-our-front-end-tests">How to Create Our Front-end Tests</h3>
<p>It's time to add some tests to our front end. Here we will do both unit and integration tests.</p>
<p><strong>Unit tests</strong><br>As I mentioned above, unit tests are suitable when we have a module or a component that doesn't have dependencies from the outside world.</p>
<p>Such components turn out to be our Username.vue and VotesChart.vue components.</p>
<p>They are representational components that receive the data they need to function properly via props. This means we can write our tests in the same manner: pass them the data they need and check whether the results of their execution are as expected.</p>
<p>Here's an important thing to mention. It's not that what is provided by the <strong>@vue/test-utils</strong> package (that comes from installing Vue.js), was not enough to test both components.</p>
<p>Rather, for educational purposes, I have decided to install and use the <a target="_blank" href="https://testing-library.com/docs/vue-testing-library/intro">Vue Testing Library</a> as well. That's why one of the components below is tested with @vue/test-utils, but the other is tested with @testing-library/vue.</p>
<p>Don't forget to install it before running the test:</p>
<pre><code class="lang-python">npm i --save-dev @testing-library/vue
</code></pre>
<p>Again, to save space, I am not going to paste the component test's code here, but you can easily see it <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/unit/Username.spec.js">here</a> and <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/unit/VotesChart.spec.js">here</a>.</p>
<p>Then in order to run the unit tests of your front-end app, go to the <code>frontend</code> folder and execute:</p>
<pre><code class="lang-python">npm run test:unit
</code></pre>
<p><strong>Integration test</strong><br>This is probably more interesting for some of you.</p>
<p>If you remember the beginning of this tutorial when we installed our Vue.js app, for our e2e (or integartion) tests solution we selected <a target="_blank" href="https://www.cypress.io/">Cypress.js</a>.</p>
<p>This is a super easy-to-use tool that allows developers to write real e2e tests for their applications by giving them immediate feedback.</p>
<p>From personal experience I would say that working with Cypress is more of a pleasure than with other similar frameworks. If you have previous experience with frameworks like Nightwatch.js or Selenium, what you see below might be familiar to you.</p>
<p>Before running our tests with Cypress, we need to do some minor changes in its configuration.</p>
<p>Find the <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/e2e/plugins/index.js">index file</a> under the <code>plugins</code> folder and add the following line to the return statement in the end of the file:</p>
<pre><code class="lang-python">  baseUrl: <span class="hljs-string">"https://localhost:5001"</span>
</code></pre>
<p>Now update the content of the test.js under the <code>specs</code> folder as it's shown <a target="_blank" href="https://github.com/mihailgaberov/pizza-app/blob/master/frontend/tests/e2e/specs/test.js">here</a>.</p>
<p>Once you have it all done, you should be able to run your e2e tests via Cypress. You can do so by executing the following command while you're in your <code>frontend</code> directory:</p>
<pre><code class="lang-python">npm run test:e2e
</code></pre>
<p>⚡Don't forget to start your back-end app before executing the e2e tests so that they work properly.</p>
<p>If you have followed along, after running the command above you should see something like this in your terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-69.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Running e2e tests</em></p>
<p>And a new browser window will be opened by Cyrpess.js itself, where you can use the provided UI to see and run your tests.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-70.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Cypress.js UI</em></p>
<p>And when all tests pass, you are supposed to see a screen like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/09/image-71.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>e2e tests pass successfully</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we have seen how to use one of the hottest technologies on the market, for both the front end and back end.</p>
<p>We've also learned how to combine them properly in order to build a small but fully functioning single page application with database support.</p>
<p>Finally, we also have written unit and integration tests for both ends.</p>
<p>I believe this kind of exercise is beneficial for both experienced and beginner readers, as it covers a lot of different stuff in a step-by-step manner. And you end up with a working example app if you've finished the entire process.</p>
<p>This tutorial ended up being much longer than I initially thought it would be. But if you have done it all, I admire you ?! And I hope it was pleasure for you reading it, as it was for me writing it.</p>
<p>? Thanks for reading! ?</p>
<h2 id="heading-resources">Resources</h2>
<p>You may find below the links that were useful to me in some way while writing this.</p>
<p>https://consultwithgriff.com/spas-with-vuejs-aspnetcore/<br><a target="_blank" href="https://github.com/okta/okta-auth-dotnet">https://github.com/okta/okta-auth-dotnet</a><br><a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test">https://docs.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-dotnet-test</a><br><a target="_blank" href="https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpresponsemessage?view=netcore-3.1">https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpresponsemessage?view=netcore-3.1</a><br><a target="_blank" href="https://vue-test-utils.vuejs.org/guides/#testing-key-mouse-and-other-dom-events">https://vue-test-utils.vuejs.org/guides/#testing-key-mouse-and-other-dom-events</a><br><a target="_blank" href="https://docs.cypress.io/guides/references/configuration.html#Options">https://docs.cypress.io/guides/references/configuration.html#Options</a><br><a target="_blank" href="https://docs.cypress.io/guides/tooling/visual-testing.html#Functional-vs-visual-testing">https://docs.cypress.io/guides/tooling/visual-testing.html#Functional-vs-visual-testing</a><br><a target="_blank" href="https://www.codingame.com/playgrounds/35462/creating-web-api-in-asp-net-core-2-0/part-3---integration-tests">https://www.codingame.com/playgrounds/35462/creating-web-api-in-asp-net-core-2-0/part-3---integration-tests</a><br><a target="_blank" href="https://testing-library.com/docs/vue-testing-library/intro">https://testing-library.com/docs/vue-testing-library/intro</a><br>https://www.valentinog.com/blog/canvas/</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create an Air Blowing Effect with JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wondered how you can create a realistic air blowing effect with JavaScript? Like the one shown on the evening TV shows, where multiple balls are being mixed up in a sphere-like object by leveraging air pressure? If you want to find out ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-lotto-balls-blowing-effect/</link>
                <guid isPermaLink="false">66d4604e9f2bec37e2da064e</guid>
                
                    <category>
                        <![CDATA[ animations ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Thu, 02 Jul 2020 15:36:14 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/07/ball-blower-image.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wondered how you can create a realistic air blowing effect with JavaScript? Like the one shown on the evening TV shows, where multiple balls are being mixed up in a sphere-like object by leveraging air pressure? If you want to find out how it's done, read on.</p>
<p>✨ If you want to skip the reading and jump straight to the code, you will find it <a target="_blank" href="https://github.com/mihailgaberov/bingo-blower">here</a>. Also, I have deployed a <a target="_blank" href="https://tender-hoover-fdc559.netlify.app/">live demo here</a>.✨</p>
<h2 id="heading-research">Research</h2>
<p>Recently I have decided to refurbish something old that I did 4 years ago for a <a target="_blank" href="https://github.com/mihailgaberov/bingo/">project of mine</a>. Here is how it looked:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/image-242.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Bingo Blower</em></p>
<p>At that time I chose to use a library called <a target="_blank" href="http://paperjs.org/">Paperjs</a>. Back then this library let me build the closest thing to what I wanted to achieve.</p>
<p>As it turns out, there are many more JavaScript libraries today that let you do animations with or without physics.</p>
<p>Before making my final choice, which you will see below, I played around with <a target="_blank" href="https://animejs.com/">Anime.js</a>, <a target="_blank" href="http://velocityjs.org/">Velocity.js</a>, <a target="_blank" href="https://popmotion.io/pure/">Popmotion</a>, <a target="_blank" href="https://threejs.org/">Three.js</a>, <a target="_blank" href="https://greensock.com/gsap/">GreenSock JS</a>, <a target="_blank" href="https://mojs.github.io/">Mo.js</a> and <a target="_blank" href="https://brm.io/matter-js/">Matter.js</a>. All of them have pluses and minuses, and as with everything else, your choice between them depends on the specific needs you might have. I chose Matter.js.</p>
<h2 id="heading-meet-matterjs">Meet Matter.js</h2>
<p>Matter.js is a 2d rigid body JavaScript physics engine. Sounds complex, but it's not. What this actually means is that this library contains all the stuff we need to create realistic 2d physics animations with JavaScript.</p>
<p>For detailed information on what Matter.js has to offer, you might check their <a target="_blank" href="https://brm.io/matter-js/docs/">documentation</a>. In our case, we will take advantage mainly of the <a target="_blank" href="https://brm.io/matter-js/docs/classes/Body.html">Body module</a> and the features it has. Let's see how in the next section.</p>
<h2 id="heading-balls-and-tube">Balls and Tube</h2>
<p>The "tube" component is easy. It's just a background <a target="_blank" href="https://github.com/mihailgaberov/bingo-blower/blob/master/static/images/blower.png">image</a> I am using to create an illusion that the balls are flying around inside a sphere-like glass object.</p>
<p>The interesting part is the code to create the animations and detect the collisions between the balls and the walls. But let's go step by step.</p>
<p>As I said, the "tube" is a background image I've added via the simple CSS <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/background">background property</a>. Let's see the balls themselves. For them I had two choices - try to draw circles in canvas and make them look like balls or use simple images. I chose the latter option, as I wanted to have a more realistic view of the balls.</p>
<p>So, with the help of a graphic processing program, a friend of mine created <a target="_blank" href="https://github.com/mihailgaberov/bingo-blower/tree/master/static/images">75 images</a> for me, one for each ball.</p>
<p>Having all the assets we need, we are now ready to go deeper and implement some physics with Matter.js.</p>
<h2 id="heading-implement-test-implement-test">Implement, test, implement, test</h2>
<p>Before going into the animation itself, we need to mention few Matter.js specific things. When creating animations with this library, we need to define, at a minimum:</p>
<ul>
<li><p><a target="_blank" href="https://brm.io/matter-js/docs/classes/Engine.html">Matter.Engine</a> - this is the controller that manages updates to the simulation of the world.</p>
</li>
<li><p><a target="_blank" href="https://brm.io/matter-js/docs/classes/World.html">Matter.World</a> - contains methods for creating and manipulating the world composite.</p>
</li>
<li><p><a target="_blank" href="https://brm.io/matter-js/docs/classes/Render.html">Matter.Render</a> - this module is a simple HTML5 canvas-based renderer for visualizing instances of <code>Matter.Engine</code>.</p>
</li>
</ul>
<p>In our example we are also going to use:</p>
<ul>
<li><p><a target="_blank" href="https://brm.io/matter-js/docs/classes/Bodies.html">Matter.Bodies</a> for creating the different parts of the scene (the balls, the invisible boundary circle).</p>
</li>
<li><p><a target="_blank" href="https://brm.io/matter-js/docs/classes/Body.html">Matter.Body</a> for applying forces to the bodies and thus creating a nice physics-based simulation of a blower.</p>
</li>
<li><p><a target="_blank" href="https://brm.io/matter-js/docs/classes/Runner.html">Matter.Runner</a> to run the whole thing.</p>
</li>
<li><p><a target="_blank" href="https://brm.io/matter-js/docs/classes/Events.html">Matter.Events</a> gives us ability to have listeners for different events that could happen during the animation. In this specific case we use it for listening for the 'tick' event, which occurs on every render tick.<br>  In the event handler function we do our checking for when the balls collide with the walls and we apply the relevant forces to create a bounce effect.<br>  We postpone the listening for this event with a 3 second timeout, so we can have a more lotto-like effect. Imagine a sphere where the balls are starting to move when, let's say, a button is pressed.</p>
</li>
</ul>
<h2 id="heading-try-and-play">Try and Play</h2>
<p>In the beginning of this article I posted the link to the <a target="_blank" href="https://github.com/mihailgaberov/bingo-blower">GitHub repository</a> with the code and the assets in it. If you want to play more, you can easily check it out and try different modifications. You might want to play with the forces being applied, or the size of the balls, and so on.</p>
<p>There is plenty of room for experimenting when we talk about Physics. And it's always fun, especially when we add animations to the picture.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>As it turns out, <a target="_blank" href="https://brm.io/matter-js/index.html">Matter.js</a> is a great library for doing 2d realistic animations backed up by the laws of Physics. Of course, there are other options you might choose from, but as I said, this is a matter of choice and project needs.</p>
<p>I personally would recommend at least giving it a try and see for yourself. For someone with Flash experience or similar, Matter.js is definitely easy to start with. And if you are stubborn enough to keep trying different settings, you might achieve incredible results.</p>
<h2 id="heading-resources">Resources</h2>
<p>https://brm.io/matter-js/ - The website of the library<br><a target="_blank" href="https://burakkanber.com/blog/modeling-physics-in-javascript-introduction/">https://burakkanber.com/blog/modeling-physics-in-javascript-introduction/</a> - interesting and well explained articles related to physics in JavaScript<br><a target="_blank" href="https://spicyyoghurt.com/tutorials/html5-javascript-game-development/collision-detection-physics/">https://spicyyoghurt.com/tutorials/html5-javascript-game-development/collision-detection-physics/</a> - collisions detection tutorial<br><a target="_blank" href="https://codepen.io/AlexRA96/full/egaxVV">https://codepen.io/AlexRA96/full/egaxVV</a> - bouncing ball example<br><a target="_blank" href="https://codepen.io/Shokeen/pen/WjKmMG?editors=1010">https://codepen.io/Shokeen/pen/WjKmMG?editors=1010</a> - codepen example with applying forces<br><a target="_blank" href="https://code.tutsplus.com/tutorials/getting-started-with-matterjs-body-module--cms-28835">https://code.tutsplus.com/tutorials/getting-started-with-matterjs-body-module--cms-28835</a> - beginner tutorial to get you started with Matter.js<br><a target="_blank" href="https://codepen.io/jh3y/pen/gOPmMyO?editors=0110">https://codepen.io/jh3y/pen/gOPmMyO?editors=0110</a> - another cool example with falling bears<br><a target="_blank" href="https://codepen.io/danielgivens/pen/geKrRx">https://codepen.io/danielgivens/pen/geKrRx</a> - even cooler example with a circle clock and particles inside<br><a target="_blank" href="https://codepen.io/dotcli/pen/NEXrQe">https://codepen.io/dotcli/pen/NEXrQe</a> - another example of circle bounds and particles (socks!) inside</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I built a desktop chat app with CometChat and NW.js (and how you can too) ]]>
                </title>
                <description>
                    <![CDATA[ This is not your typical "paste this here" and "paste that there"-type tutorial (you can find plenty of those here on cometchat.com/tutorials). While those tutorials certainly have merit, I am going to share my thought process from beginning to end. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-build-a-desktop-chat-app-with-cometchat-and-nw-js-and-how-you-can-too/</link>
                <guid isPermaLink="false">66d4604633b83c4378a51808</guid>
                
                    <category>
                        <![CDATA[ Apps ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Sun, 22 Sep 2019 11:38:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/09/mihail-chat-app-image.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This is not your typical "<em>paste this here</em>" and "<em>paste that there</em>"-type tutorial (you can find plenty of those here on <a target="_blank" href="https://www.cometchat.com/tutorials/desktop-chat-app-tutorial/">cometchat.com/tutorials</a>). While those tutorials certainly have merit, I am going to share my thought process from beginning to end.</p>
<p>The application I built is simple enough. When someone loads the app, they are prompted to enter their username and begin chatting:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/image-143.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Welcome Screen</em></p>
<p>The application ultimately runs on Node with help from NW.js (previously known as node-webkit). NW.js is advantageous because it enables us to code cross-platform desktop applications using our favorite web technologies. For this application, I chose to use React and Redux.</p>
<p>The back-end logic - from sending and receiving messages in real-time to populating what I call the "participant list" - is powered by CometChat. You will learn more about <a target="_blank" href="https://cometchat.com/pro">CometChat</a> as you read on.</p>
<p>This post is not intended to be a walkthrough. Although I will be explaining the technical components of this application, my main ambition is to help you think through a solution from beginning to end. Hopefully, when you finish this post you'll be a slightly better developer and consider CometChat for your growing tool belt.</p>
<p><strong>Just want the example code?</strong></p>
<p>You may see the source code <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw">here</a>. There is also a detailed <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/blob/master/README.md">README</a>, where you will find all the information you need to install, run and test the app.</p>
<p>Because the desktop app is built using web technologies, it is entirely possible to run it in your browser. Towards the end of this post, I will show you how to deploy the app on Netlify.</p>
<h2 id="heading-planning"><strong>Planning</strong></h2>
<p>In this section we have to decide what components will we need to build. What functionality will they have? In other words, what are the questions we need to answer, to plan the building process?</p>
<p>Let’s step back for a while and think. Try asking yourself the questions who will take us to the structure we need for our app.</p>
<p><em>Below I am going to lay out my questions and the answers. This is the process of the actual building the structure of the app, but in your head first. Keep in mind that it happens very often such that when answering a question new questions appear. This was the case with me as well.</em></p>
<p><strong>Questions:</strong></p>
<ul>
<li><p>What am I doing? ?</p>
</li>
<li><p>What kind of app am I going to be building?</p>
</li>
<li><p>What are the most common components, such an app needs to have?</p>
</li>
<li><p>How do the app’s components interact with each other?</p>
</li>
<li><p>What level of completion am I aiming for — (demo apps are not supposed to be fully featured)?</p>
</li>
</ul>
<p><strong>Answers</strong> (following the order of the questions):</p>
<ul>
<li><p>This is the most neglected question that many people forget to think about. <em>When one can step aside first and give a clear answer to this question, his path for future developments becomes settled</em>. In my specific case the answer I got sounds something like this — “I am thinking about building a chat app. This app should serve as a tutorial demo app. It will have to provide basic functionality for ‘having a chat’ by using CometChat API. It should run on a desktop”. The styling and specific details about what goes where will come later in the process.</p>
</li>
<li><p>A chat app that will run on desktop and serve as a demo for this tutorial.</p>
</li>
<li><p>To give a proper answer to this question, a non-familiar person would have to do some research first. Take a look at real-world chat applications. Make notes of what features they have. How are they put them in place, how do they interact between them and with the users of the app. In my case, I had some <a target="_blank" href="https://mihail-gaberov.eu/how-i-build-chat-app-with-react-and-typescript-part1/">previous experience</a> and got, more or less, the idea of what I need.</p>
</li>
<li><p>The interaction between the components would be pretty straight forward. The user should be able to use the main component that is a text input and a button to send messages. And a sidebar component to see the other chat participants.</p>
</li>
<li><p>The demo app should provide basic chat functionality — send and receive real-time messages. And be able to run on a desktop (without a browser).</p>
</li>
</ul>
<h2 id="heading-features"><strong>Features</strong></h2>
<p>I have decided to implement the following features to the demo app:</p>
<ul>
<li><p>Send with Enter key</p>
</li>
<li><p>A sidebar with names and last active time</p>
</li>
<li><p>Welcome screen with input and validation with error messages</p>
</li>
<li><p>Chat area with auto-scroll to bottom</p>
</li>
<li><p>Chat message and time of sending.</p>
</li>
</ul>
<h2 id="heading-front-end-react"><strong>Front End — React</strong></h2>
<p>We are going to use <a target="_blank" href="https://reactjs.org/">React</a> to build our user interface. Below, I am going to list the components I have created and a short explanation about each of them:</p>
<ul>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/tree/master/src/components/ChatPane">ChatPane</a> — this is the main container-like component that contains the Participants and Conversation components and passes the data they need to visualize.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/blob/master/src/components/Conversation/Conversation.jsx">Conversation</a> — this is the component responsible for typing and sending chat messages.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/blob/master/src/components/Footer/Footer.jsx">Footer</a> — displays a simple footer message, containing the app name and version, as defined in package.json file.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/blob/master/src/components/Header/Header.jsx">Header</a> — header component holding the application menu bar.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/blob/master/src/components/MenuAppBar/MenuAppBar.jsx">MenuAppBar</a> — application menu bar component, simulating how a real menu bar would look like. The hamburger menu on the left and the profile dropdown menu on the right are fake — clickable, but not functional.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/blob/master/src/components/Messages/Messages.jsx">Messages</a> — a container component, holding a message itself — it has the name of the sender, the message content and the time of sending.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/tree/master/src/components/Participants">Participants</a> — this component shows the name of a chat member and the time when he joined.</p>
</li>
<li><p><a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/tree/master/src/components/Welcome">Welcome</a> — this component is responsible for displaying the login page — the starting point of our app, where we have logic related to checking for certain allowed usernames and storing them to the local storage for later use. I also implemented basic error handling logic, which shows an error when the selected username is not correct, as per CometChat API (in this specific case for our demo) registered usernames — superhero1, superhero2 and so on till 5.</p>
</li>
</ul>
<p>Here a visual representation of the app components:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/image-144.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Visual Components</em></p>
<h2 id="heading-state-management-redux"><strong>State Management — Redux</strong></h2>
<p>Every modern application these days has a state. Place in the memory where the application is storing certain data for later use. For our application state management, we use <a target="_blank" href="https://redux.js.org/">Redux</a>. Of course, for a simple app like this, we could go without using Redux at all. But, from the learning point of view (<em>after all we all do this for learning new stuff, right?</em>), I think it would be nice to see the whole cycle of sending a request to an API, going through a middleware (redux-thunks) and getting the response recorded to the state. And we will manage this state by using Redux.</p>
<h3 id="heading-how-it-works"><strong>How it works</strong></h3>
<p>The main building blocks in a Redux app are called reducers — small functions used for managing the state. Simply said, what they do is accepting the old state object as input and, depending on certain actions (which are also passed in the same input), returning new state object. The new state could be changed in full or just partially.</p>
<p>In our app, we have three simple reducers, which are responsible for those parts of the state, responsible for the users’ list, the login process and sending/receiving messages. All of them can be seen in <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/tree/master/src/reducers">/src/reducers</a> folder, along with a <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/blob/master/src/reducers/initialState.js">file</a> containing the initial state of our app.</p>
<p>Redux, as state management library, can be used with any other UI framework, practically every app that needs to have a state can benefit from using Redux. If you want to go deeper, start from their website and follow along.</p>
<h2 id="heading-side-effects-handling-redux-thunks"><strong>Side Effects Handling — Redux Thunks</strong></h2>
<p>One of the best known approaches for managing side effects in a redux app is called <a target="_blank" href="https://github.com/reduxjs/redux-thunk">redux-think</a>. This is what we use in our application as well. If you want to learn more details about redux thunks and how to use them, I recommend their website as a starting point and then build a small application, like this one for example :).</p>
<p>In our project, in <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/tree/master/src/actions">/src/actions folder</a>, is where I put the thunks used in the demo application. And in <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/tree/master/src/store">/store</a> directory is where the configurations for the redux store live.</p>
<h2 id="heading-make-it-desktop-nwjs"><strong>Make it desktop — NW.js</strong></h2>
<p>The part of our application that makes it possible for our app to run on desktop is taken care of by a library called <a target="_blank" href="https://nwjs.io/">NW.js</a>. Remember that we are building a desktop application. Exactly the desktop part is going to be implemented via NW.js. Similar to <a target="_blank" href="https://electronjs.org/">Electron</a>, another library for building desktop applications, NW.js provides a way to the developers to use their web skills to build applications that can run on a desktop. This means you can still use your JavaScript/React skills when building an app and then leverage the power of the desktop operating system via Nw.js APIs. In other words, Nw.js gives you the ability to make a skeleton app, which can be “filled” with your UI, no matter what library you have used to create it. And the best thing is that such an app has access to Node.js/NW.js APIs and the DOM in the same JavaScript context.</p>
<p>Since we mentioned the other big player in the field of building cross-platform desktop apps, let me give you a brief comparison between the two.</p>
<h2 id="heading-nwjs-vs-electron"><strong>Nw.js vs Electron</strong></h2>
<p>Entry of Application</p>
<ul>
<li><p>In NW.js the main entry point of an application is a web page or a JS script. You specify an HTML or js file in the package.json and it is opened in a browser window as the application's main window (in case of an HTML entrypoint) or the script is executed.</p>
</li>
<li><p>In Electron, the entry point is a JavaScript script.</p>
</li>
</ul>
<p>Build System</p>
<ul>
<li><p>Nw.js uses Chromium</p>
</li>
<li><p>Electron uses <a target="_blank" href="https://github.com/electron/libchromiumcontent">libchromiumcontent</a> to access Chromium's Content API. libchromiumcontent is a single shared library that includes the Chromium Content module and all of its dependencies.</p>
</li>
</ul>
<p>Node Integration</p>
<ul>
<li><p>In NW.js, the Node integration in web pages requires patching Chromium to work.</p>
</li>
<li><p>In Electron uses a different way to integrate the libuv loop with each platform's message loop to avoid hacking Chromium.</p>
</li>
</ul>
<p>Multi-context</p>
<ul>
<li><p>Because of how NW.js was implemented concepts of Node context and web context were invented.</p>
</li>
<li><p>By using the <a target="_blank" href="https://github.com/nodejs/node-v0.x-archive/commit/756b622">multi-context</a> feature of Node, Electron doesn't introduce a new JavaScript context in web pages.</p>
</li>
</ul>
<h2 id="heading-chat-cometchat"><strong>Chat — CometChat</strong></h2>
<p>The usage of CometChat API is pretty straight-forward. It’s a RESTFull API, on which is built another layer of abstraction - CometChat SDK. It allows us to call directly exposed methods for different actions we might want to perform, such as send. Here an example of such a method:</p>
<pre><code class="lang-js"><span class="hljs-keyword">return</span> CometChat.sendMessage(textMessage).then(    
  <span class="hljs-function"><span class="hljs-params">message</span> =&gt;</span> {      
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Message sent successfully:"</span>, message);      
    <span class="hljs-keyword">return</span> message;
  }, 
  <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> {      
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Message sending failed with error:"</span>, error);    
  }
);
</code></pre>
<p>You may see all the Chat API logic in <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/tree/master/src/chat-api">/src/chat-api</a> folder. There you will also see the mocks I created, which allow us to test our app without real connection to the API.</p>
<h2 id="heading-improvements"><strong>Improvements</strong></h2>
<p>Every project deserves some thoughts after finishing the first phase. One part of this thought process would be dedicated to how it went, what was good and bad, and what might be done better. And one part would be dedicated to thinking about possible improvements. Here are a few ideas for our case. If someone goes to this direction and implement any of these for real, please do not forget to let me know :)</p>
<ul>
<li><p>Waiting animation for when loading the chat history and the user list</p>
</li>
<li><p>Option for skipping the login screen, if already logged</p>
</li>
<li><p>Option for sending invitations to new users</p>
</li>
<li><p>Option for seeing the status of a message — sent, received, read</p>
</li>
<li><p>Emojis support</p>
</li>
<li><p>Inline links/images/video support, such that the user can see them interpreted — playing video, rendered image or web page to which a link is pointing. I have added these as <a target="_blank" href="https://github.com/mihailgaberov/desktop-chat-nw/issues">issues in my GitHub</a>, in case anyone wants to take a look.</p>
</li>
</ul>
<h2 id="heading-deploy-on-netlify"><strong>Deploy on Netlify</strong></h2>
<p>To deploy your application to Netlify platform you need to create an account first. Go to <a target="_blank" href="https://www.netlify.com/">their website</a> and sign up for new account. After that go ahead and login. While still under Sites section, you should see a button for deploying new site from Git.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/image-145.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click it and follow the steps to create a new site for deployment from your GitHub repositories. Your process should be similar to what is shown in the image below.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/image-146.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, the last step before having your app deployed is to make sure you have the correct build commands and environment variables in place. To do that, after you create your site for deployment, go to <strong>Build &amp; deploy</strong> settings screen and enter the following (don’t forget to use your repo URL):</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/image-147.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Under <strong>Environment</strong> section is where you need to enter the environment variables as defined in your .env file. Here is how it looks mine:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/image-148.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Note: <em>I have erased the values as this is supposed to be private info and you should not share yours as well.</em></p>
<p>That should be enough for you to have your app deployed on Netlify. Keep in mind the <strong>Deploys</strong> default settings are set to ‘auto publishing’, which means that it will trigger a deploy on each commit you do to the <strong>master branch</strong> in your repo. This is the place where you can trigger a deploy manually as well. This is how my <strong>Deploys</strong> screen looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/image-149.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we saw how can we leverage our web development skills to create applications that can run on a desktop. What we built is a demo application, that lacks a lot of a fully-featured-production-ready app features, but when one wants to learn and share, this would do the job. If you would like to deepen your knowledge in this direction, I would recommend you to try to improve it, by implementing features that are more likely to be seen in a real desktop application.</p>
<p>There are plenty of opportunities to explore out there, I hope this tutorial entertained you enough to keep your flame of curiosity burning even more.</p>
<p>? Thanks for reading! ?</p>
<p><strong>Notes:</strong></p>
<ul>
<li>In order to use Nw.js DevTools you need to install the SDK build —  <a target="_blank" href="https://nwjs.io/downloads/">https://nwjs.io/downloads/</a> - version 0.38.2 or higher.</li>
</ul>
<p><strong>Resources:</strong></p>
<ul>
<li><p><a target="_blank" href="https://daveceddia.com/what-is-a-thunk/">Q: What is a ‘thunk’? A: The sound your head makes when you first hear about redux-thunk. Ok sorry, that was awful. But…daveceddia.com</a></p>
</li>
<li><p>[book] Cross-Platform Desktop Applications: Using Node, Electron, and NW.js</p>
</li>
<li><p>[book] Cross-platform Desktop Application Development: Electron, Node, NW.js, and React</p>
</li>
<li><p><a target="_blank" href="https://github.com/reduxjs/redux-thunk">Thunk middleware for Redux</a></p>
</li>
<li><p><a target="_blank" href="https://reactjs.org/docs/hooks-reference.html#useref">https://reactjs.org/docs/hooks-reference.html#useref</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Visualize the Pancakes Algorithm with React and Popmotion.io ]]>
                </title>
                <description>
                    <![CDATA[ What you are going to see below was supposed to be part of my solution to an exercise given in a coding challenge. It was several months ago, and I had signed in for it. Due to unforeseen factors, I hadn't finished it. Now, after that time and the ch... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/visualize-pancakes-algorithm-with-react-and-popmotion-io/</link>
                <guid isPermaLink="false">66d460609f2bec37e2da0656</guid>
                
                    <category>
                        <![CDATA[ algorithms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Job Interview ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Tue, 03 Sep 2019 12:39:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/09/pancakes-algorithm-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><img src="https://www.freecodecamp.org/news/content/images/2019/09/pancakes-algorithm.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>What you are going to see below was supposed to be part of my solution to an exercise given in a coding challenge. It was several months ago, and I had signed in for it. Due to unforeseen factors, I hadn't finished it. Now, after that time and the challenge is finished, I can share it here.</p>
<p>This is not going to be a step-by-step tutorial. Rather, it'll be a quick review of how can we use frameworks like React and Popmotion.io along with an algorithm. And then create a nice visualization of that very same algorithm. Somehow it feels nice! ?</p>
<p>The so called <a target="_blank" href="https://en.wikipedia.org/wiki/Pancake_sorting"><em>Pancakes Sorting Algorithm</em></a> is a famous (or not?) sorting algorithm that you can read more about on the internet, if you're interested. Its nature is out of the scope of this article. Here we only see it in action with nice animations, thanks to Popmotion.io.</p>
<p>Here is the <a target="_blank" href="https://pancakes-algorithm.herokuapp.com/"><em>live demo</em></a> you can play with. There are two text inputs and two buttons. In the first input you enter the time interval which will be used for each animation round, that is how fast each pancake will be sorted. It's in milliseconds, which means if you enter the value 1000, the animation will execute for roughly 1 second.</p>
<p>The second input is used to define how many pancakes you want to see sorting. The value there must be between 2 and 50. The buttons are self-explanatory enough. One is for starting the sorting animation, the other one is for resetting it.</p>
<p>And <a target="_blank" href="https://gitlab.com/mihailgaberov/pancake-algorithm-visualizer"><em>here</em></a> is where you can find the source code for the demo app. Feel free to check it out and take a closer look. You might try to amend the animations I did. I would be more than interested to see your versions. :)</p>
<p>That was all. Nice and short, perfect for the summer! ☀️ ?</p>
<p>? Thanks for reading! ?</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to optimize your JavaScript app by using service workers ]]>
                </title>
                <description>
                    <![CDATA[ Every now and then we hear about optimizing something. There are different kinds of optimizations we can do to make our apps faster and more efficient, or to save time or memory. This article will cover one of those methods — s_ervice_ w_orkers._ TL;... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/optimize-your-javascript-app-by-using-service-workers/</link>
                <guid isPermaLink="false">66d46058264384a65d5a95bc</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ optimization ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Mihail Gaberov ]]>
                </dc:creator>
                <pubDate>Mon, 02 Sep 2019 12:31:08 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5f9ca08e740569d1a4ca4963.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Every now and then we hear about optimizing something. There are different kinds of optimizations we can do to make our apps faster and more efficient, or to save time or memory. This article will cover one of those methods — s_ervice_ w_orkers._</p>
<h3 id="heading-tldr">TL;DR</h3>
<p>This tutorial explains what a <em>service worker</em> is and how to use it, in JavaScript. There is a code example at the end of it. If you want to skip the reading, <a target="_blank" href="https://github.com/mihailgaberov/learn-service-workers">here</a> is the Git repo and <a target="_blank" href="https://compassionate-brahmagupta-71d9b4.netlify.com/">here</a> you may see a live demo.</p>
<h3 id="heading-the-theory">The Theory</h3>
<p>Let’s see first what a w_orker_ is this ? and what s_ervice_ can we use it for ?.</p>
<p>The <em>service worker</em> is a <a target="_blank" href="https://developers.google.com/web/fundamentals/primers/service-workers/">simple script</a>. It's JavaScript code, that your browser runs in the background, separate from a web page.</p>
<p>It’s very convenient to use service workers for features that don’t need a web page or user interaction. One of the most common uses is intercepting and handling network requests. This includes managing a cache of responses.</p>
<p>The following is a simple example of how to include a service worker in your application.</p>
<p>Usually, in the entry point of your app, you add this code:</p>
<pre><code class="lang-js"><span class="hljs-keyword">if</span> (<span class="hljs-string">'serviceWorker'</span> <span class="hljs-keyword">in</span> navigator) {  
    <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'load'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        navigator.serviceWorker.register(<span class="hljs-string">'/service-worker.js'</span>);  
    });
}
</code></pre>
<p>This way of using service workers is a little bit improved than the basic one. The basic method involves directly calling the <em>register</em>() method inside the <em>if statement.</em> In this case, we use the window load event to register the service worker after the page has finished loading. After doing this, you need to add your service worker code in the <em>service-worker.js</em> file. At this point, you might want to take a look at my service worker file.</p>
<p><em>All major browsers support Service Workers now, and you can start using them right away.</em></p>
<h3 id="heading-the-example">The Example</h3>
<p>Enough theory, let’s build a real example app that will leverage the service workers feature.</p>
<p>Let’s imagine we are building an app that needs to load a big chunk of data. It could be, for example, a nice, big full-screen image we display on the front page. Or it could be a big video clip we have to wait to load. This is an ideal use case for a service worker to shine. Let’s see how. ?</p>
<p>In our specific case, we will use the clock time to show the benefit of using service workers. What I mean is, that we will build a simple app, showing the time. It will have a nice, big button for fetching a nice, big image. And it will provide the user with an option to choose <strong>to use or not</strong> a service worker.</p>
<p>Here is a screenshot of how it looks:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/09/1_2K-IvfpcK017rGsX1Hsm8w.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>What this app demonstrates is, that when fetching the image (by clicking the button, wow!) with an active service worker — we don’t get blocked UI (user interface, i.e. fields, buttons, ?). If you choose not to use the service worker, you will get a frozen UI for a certain period of time. When the work completes and the main thread frees itself, it will unfreeze the UI.</p>
<p>If you don’t want to clone and run the code yourself, jump straight to the <a target="_blank" href="https://compassionate-brahmagupta-71d9b4.netlify.com/">live demo</a>.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>This demo of service workers in action shows us the advantage we get from using them. Especially when you are trying to build responsive and robust JavaScript applications. No user wants to end up in a frozen page for an unknown time, as no developer should want that for his application’s users. Keeping in mind the above, service workers are a <em>must</em> now. And we should not neglect them.</p>
<p>? Thanks for reading! ?</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
