<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Frank Pierce</title>
    <description>The latest articles on DEV Community by Frank Pierce (@llleeeaaannn).</description>
    <link>https://dev.to/llleeeaaannn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1009536%2F92a4719c-083b-4b6d-8486-5b7b9d37daba.png</url>
      <title>DEV Community: Frank Pierce</title>
      <link>https://dev.to/llleeeaaannn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/llleeeaaannn"/>
    <language>en</language>
    <item>
      <title>Short-Circuit Evaluation &amp; Conditional Rendering in React</title>
      <dc:creator>Frank Pierce</dc:creator>
      <pubDate>Wed, 25 Jan 2023 04:53:11 +0000</pubDate>
      <link>https://dev.to/llleeeaaannn/short-circuit-evaluation-conditional-rendering-in-react-2053</link>
      <guid>https://dev.to/llleeeaaannn/short-circuit-evaluation-conditional-rendering-in-react-2053</guid>
      <description>&lt;p&gt;&lt;em&gt;Conditional rendering in React is great, here’s a tip to make sure you don’t get caught out by Javascript’s short-circuit evaluation.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It works!
&lt;/h2&gt;

&lt;p&gt;Ever tried to use conditional rendering to hide a component in React when a variable or expression is falsy? I do it all the time, but sometimes Javascript’s short-circuit evaluation catches me out. Let’s have a look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ randomVariableOrExpression &amp;amp;&amp;amp;
  &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The way we, or at least I, expect the above (and conditional rendering in general) to work is that if the variable/expression we evaluate is truthy, the component is rendered. And if it’s falsy, the component isn’t rendered.&lt;/p&gt;

&lt;p&gt;Simple right?&lt;/p&gt;

&lt;p&gt;We expect the behaviour I outlined above because it’s what happens 99% of the time. But there’s a catch.&lt;/p&gt;

&lt;p&gt;Conditional rendering as outlined above functions because if the first value (&lt;em&gt;randomVariableOrExpression&lt;/em&gt;) is falsy, the expression will short-circuit and simply return the falsy value.&lt;/p&gt;

&lt;p&gt;React doesn’t render &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;undefined&lt;/code&gt;, or &lt;code&gt;true&lt;/code&gt; so nothing is rendered as expected, usually.&lt;/p&gt;

&lt;h2&gt;
  
  
  It Doesn’t Work?
&lt;/h2&gt;

&lt;p&gt;However, if you were to write the below code where &lt;code&gt;zeroVariable&lt;/code&gt; evaluates to the number &lt;code&gt;0&lt;/code&gt; , React will render 0 in your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ zeroVariable &amp;amp;&amp;amp;
  &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What? That’s not what you told it to do. You said, “If the value is falsy, render nothing”. This is where you either decide that React is broken and you’re the first one to notice (unlikely) or you’ve missed something.&lt;/p&gt;

&lt;p&gt;What you’ve missed is that a logical expression which short-circuits is evaluated to the falsy value, not to false.&lt;/p&gt;

&lt;p&gt;So in most cases, where the logical expression evaluates to &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt; you’ll see what you expect as React doesn’t render these values.&lt;/p&gt;

&lt;p&gt;But there are values which are falsy that React does render, namely &lt;code&gt;0&lt;/code&gt; , &lt;code&gt;-0&lt;/code&gt; and &lt;code&gt;NaN&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I first encountered this issue when I attempted to conditionally render some elements depending on whether an array was empty. My code was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ array.length &amp;amp;&amp;amp;
    &amp;lt;div&amp;gt;
      array.map((item, i) =&amp;gt; (
        &amp;lt;span&amp;gt;{ item }&amp;lt;/span&amp;gt;
      ))
    &amp;lt;/div&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looked right to me. If the array has any items in it then &lt;code&gt;array.length&lt;/code&gt; will be truthy and React will render my elements. If it’s empty then &lt;code&gt;array.length&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt; which is falsy and React won’t render anything.&lt;/p&gt;

&lt;p&gt;As I’m testing my application, I see 0’s rendered everywhere.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“What the heck”&lt;/em&gt;, I thought.&lt;/p&gt;

&lt;p&gt;I eventually figured out what I just explained to you. This doesn't only apply to array lengths. A variable could have a value of &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;NaN&lt;/code&gt; and unexpectedly render in your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ohhh, It Works!!
&lt;/h2&gt;

&lt;p&gt;As you may know, &lt;code&gt;!&lt;/code&gt; evaluates a value to &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt; and inverts it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;!true == false
!false == true
!1 == false
!0 == true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a second &lt;code&gt;!&lt;/code&gt; and it’ll revert it to its original boolean value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;!!true == true
!!false == false
!!1 == true
!!0 == false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All you have to do is prepend your expression or variable with &lt;code&gt;!!&lt;/code&gt; (that's two exclamation points to be clear).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ !!zeroVariable &amp;amp;&amp;amp;
  &amp;lt;h1&amp;gt;Hello&amp;lt;/h1&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guarantees your falsy values (including &lt;code&gt;0&lt;/code&gt; , &lt;code&gt;-0&lt;/code&gt; and &lt;code&gt;NaN&lt;/code&gt; ) will resolve to &lt;code&gt;false&lt;/code&gt; (and be ignored by React) while keeping any truthy values as &lt;code&gt;true&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to get in touch, can find me at &lt;a href="https://www.frankpierce.me/" rel="noopener noreferrer"&gt;frankpierce.me&lt;/a&gt; or email me at &lt;a href="mailto:frank.pierceee@gmail.com"&gt;frank.pierceee@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>Vanilla Service Workers in React</title>
      <dc:creator>Frank Pierce</dc:creator>
      <pubDate>Wed, 18 Jan 2023 12:07:07 +0000</pubDate>
      <link>https://dev.to/llleeeaaannn/vanilla-service-workers-in-react-138</link>
      <guid>https://dev.to/llleeeaaannn/vanilla-service-workers-in-react-138</guid>
      <description>&lt;p&gt;&lt;em&gt;The following article explains how to make your React web application offline compatible and PWA compliant through Service Workers without using the countless libraries that are often prescribed, specifically how to cache files with dynamic names. If you’re in a rush, the secret is in Step 7.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I will presume that you have an understanding of what a Progressive Web Application (PWA) is and how Service Workers are used in PWAs. If you’re not familiar with these concepts (or need a refresher), I highly recommend the following resources:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=cmGr0RszHc8" rel="noopener noreferrer"&gt;Jake Archibald’s riveting talk on Progressive Web Apps and Service Workers&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://web.dev/progressive-web-apps/" rel="noopener noreferrer"&gt;Web.dev’s Progressive Web App Tutorial&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API" rel="noopener noreferrer"&gt;Mozilla’s Service Worker Documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If, like me, you marvelled at the features that Progressive Web Applications bestow then you likely immediately tried to integrate them into your projects.&lt;/p&gt;

&lt;p&gt;The project that I decided to refactor into a PWA was a lightweight React web application which converts concert setlists into Spotify playlists. My main goal was to forge my application into a PWA which would display while the user is offline due to the service worker serving cached files.&lt;/p&gt;

&lt;p&gt;Three attributes are required to qualify as a Progressive Web Application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTPS — All PWAs must run on HTTPS as many core PWA technologies, such as service workers, require HTTPS.&lt;/li&gt;
&lt;li&gt;manifest.json — Manifest.json files are required as they provide vital information to the browser regarding how it should behave when installed on the user’s desktop or mobile device. They contain data such as icon names, background colours, display preferences…&lt;/li&gt;
&lt;li&gt;Service Worker — Service Workers are necessary for many of the prominent PWA features such as Offline First, Background Sync and Notifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hosting your web application on HTTPS and writing a manifest.json file are both easy and well-documented processes.&lt;/p&gt;

&lt;p&gt;And, while more technical, creating a Service Worker which caches files and serves them from the cache while the user is offline (and even online if the files are available) is reasonably straight forward too. Or, so I thought until I ran into cache-busting hashes.&lt;/p&gt;

&lt;p&gt;Our Service Worker will have 3 purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache the app’s static assets (HTML, CSS, JS, Fonts…) in the browser’s cache.&lt;/li&gt;
&lt;li&gt;Delete old caches when a new version becomes available.&lt;/li&gt;
&lt;li&gt;Intercept fetch requests and serve assets from the cache when available. Otherwise, fetch as normal from the network.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Take the Red Pill
&lt;/h2&gt;

&lt;p&gt;If you Google ‘How to make React App into offline-first PWA’, you’ll be bombarded with countless helpful articles and tutorials. Most, if not all, of these will tell you to install any number of libraries and external packages all of which require you to refactor much of your project to complete the task.&lt;/p&gt;

&lt;p&gt;Now, I love libraries. They’re open-source, optimised, reliable code written by experts and provided to you for free.&lt;/p&gt;

&lt;p&gt;However, the primary drive behind my development at the time was to learn. Sure I could import 5 npm packages, write a few lines of code and have a fully functioning PWA. But where’s the fun (or learning) in that?&lt;/p&gt;

&lt;p&gt;Instead, let’s write our own Service Worker for React.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Registering and Creating our Service Worker
&lt;/h2&gt;

&lt;p&gt;React CRA provides your project with a file which creates and registers a service worker. If your project doesn't have it you can find it &lt;a href="https://github.com/cra-template/pwa/blob/main/packages/cra-template-pwa/template/src/serviceWorkerRegistration.js" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can write this yourself, as I have previously, but React’s template is excellent and allows us to focus on the fun stuff.&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;public&lt;/code&gt; folder, create a file called &lt;code&gt;service-worker.js&lt;/code&gt;. This will be where all our service worker code goes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Caching Assets
&lt;/h2&gt;

&lt;p&gt;For our application to function offline, we must cache all crucial static files. To do this we’ll create a cache name, an array of files to cache and an Event Listener which caches the files. My &lt;code&gt;service-worker.js&lt;/code&gt; begins as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const CACHE_NAME = "spotlist-cache-v1";

const STATIC_ASSETS = [
  "/index.html",
  "/manifest.json",
  "/static/js/main.js",
  "/static/css/main.css",
  "/static/media/GTUltra-Regular.woff"
]

self.addEventListener('install', event =&amp;gt; {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache =&amp;gt; {
        cache.addAll(STATIC_ASSETS);
      }
    )
  );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define a cache name with the &lt;code&gt;CACHE_NAME&lt;/code&gt; variable. It’s preferable to include a version number here for reasons explained later.&lt;/p&gt;

&lt;p&gt;We then create a &lt;code&gt;STATIC_ASSETS&lt;/code&gt; array with the path to each of the files we want to add to our cache. Note that the above variable names are up to you.&lt;/p&gt;

&lt;p&gt;Finally, we add an event listener to &lt;em&gt;self&lt;/em&gt; which in this context is the service worker itself. We listen for the install event which is called when the service worker is finished installing. When the listener is triggered we do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Find or create a cache with the name in &lt;code&gt;CACHE_NAME&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add all of the files in &lt;code&gt;STATIC_ASSETS&lt;/code&gt; to said cache.&lt;/li&gt;
&lt;li&gt;Simple right? Now, whenever a user opens our application we’ll cache all of the files necessary for it to run offline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Removing Old Caches
&lt;/h2&gt;

&lt;p&gt;I mentioned earlier that your cache name should include a version number (such as &lt;em&gt;v1&lt;/em&gt;). If we a user visited our application, they’d cache our files and be stuck using them whether we changed the site’s content or not. That’s not ideal.&lt;/p&gt;

&lt;p&gt;The solution is to purge old caches and create new ones whenever the files’ content changes. To do this we’ll change the &lt;code&gt;CACHE_NAME&lt;/code&gt; version number each time we update the app’s content and create some logic which will delete caches that don't match the current version name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;self.addEventListener('activate', event =&amp;gt; {
  event.waitUntil(
    caches.keys().then(cacheNames =&amp;gt; {
      return Promise.all(
        cacheNames.map(cache =&amp;gt; {
          if (cache !== CACHE_NAME) {
            caches.delete(cache);
          }
        })
      )
    })
  )
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Akin to our previous logic, we add an event listener to our service worker and listen for the activate event. It is fired on the active service worker each time the page reloads. This is a perfect time to check if a new cache is available.&lt;/p&gt;

&lt;p&gt;We call &lt;code&gt;.keys()&lt;/code&gt; on cache to access an array of each cache name stored in the browser. We then map over each name and check if it matches the current &lt;code&gt;CACHE_NAME&lt;/code&gt;. If it doesn't, we delete that cache.&lt;/p&gt;

&lt;p&gt;Again, simple. But necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Serving from the Cache
&lt;/h2&gt;

&lt;p&gt;Successfully storing our applications files in our users' cache is an achievement. Although a futile one if we cannot access them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;self.addEventListener('fetch', event =&amp;gt; {
  event.respondWith(
    caches.match(event.request)
      .then(response =&amp;gt; response || fetch(event.request))
  );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We intercept all network requests by listening for the &lt;em&gt;fetch&lt;/em&gt; event. We then prevent the default &lt;em&gt;fetch&lt;/em&gt; behaviour with &lt;code&gt;event.respondWith()&lt;/code&gt; inside which we:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if the requested URL is in the cache — if it is, respond with it&lt;/li&gt;
&lt;li&gt;If it isn’t we fetch from the network as usual&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run a Lighthouse report on your application in Chrome Developer Tools you should find that it is now a verified Progressive Web App making it installable on phones, desktops and other devices directly from the browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwwttb8xhgh2yjwwlkdp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwwttb8xhgh2yjwwlkdp.png" alt="Lighthouse Audit" width="769" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: The Catch — Dynamic Filenames
&lt;/h2&gt;

&lt;p&gt;That seemed far too easy. Why would anyone need external libraries? Remember when I said writing Service Workers seemed easy until I ran into cache-busting hashes? Well, that’s one of the main reasons developers will install a handful of libraries.&lt;/p&gt;

&lt;p&gt;React, alongside almost all other asset pipelines, implements cache busting allowing users to receive the most recently updated files without having to perform a hard refresh or clear their browser cache.&lt;/p&gt;

&lt;p&gt;React does this by adding hashes to the filenames it outputs. The files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;main.js&lt;/li&gt;
&lt;li&gt;main.css&lt;/li&gt;
&lt;li&gt;GTUltra-Regular.woff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are renamed as below during the React build process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;main.7000985f.js&lt;/li&gt;
&lt;li&gt;main.bb03239a.css&lt;/li&gt;
&lt;li&gt;GTUltra-Regular.41205ca9d5907eb1575a.woff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While this is essential for cache busting, it throws a randomly generated wrench into our plans. We hardcoded the names of our files to be cached into &lt;code&gt;service-worker.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;How can we account for randomly generated filenames?&lt;/p&gt;

&lt;p&gt;I could abandon my mission to write my own React-compatible Service Worker and flee to the comfort of expertly crafted libraries such as Google’s Workbox.&lt;/p&gt;

&lt;p&gt;Or I could find my own solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: The Solution
&lt;/h2&gt;

&lt;p&gt;I turned to Google for help. I figured many others before must have run into the same issue. Predictably, I found a multitude of Stackoverflow questions, Reddit posts and GitHub issues on the subject. But none had anything resembling a concrete answer (other than to disable the cache busting hashes — something I hoped to avoid).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you’re overwhelmed by the size of a problem, break it down into smaller pieces.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And so I did. Here are the smaller pieces I came up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React’s build process adds an unknowable hash to certain filenames.&lt;/li&gt;
&lt;li&gt;Our Service Worker needs the name, hashes included, of each filename.&lt;/li&gt;
&lt;li&gt;The Service Worker is written before the build process occurs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Likewise, here are some facts I knew or found through research which may help us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React’s build process creates &lt;code&gt;asset-manifest.json&lt;/code&gt;. A file containing the paths of all generated assets, hashes included.&lt;/li&gt;
&lt;li&gt;We can run scripts directly following React’s build process by appending them to the build command in &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So here’s the theory: we can write a script, which runs directly after the build process, that parses &lt;code&gt;asset-manifest.json&lt;/code&gt; for the hashed filenames and modifies &lt;code&gt;service-worker.js&lt;/code&gt; to include them in our filenames array.&lt;/p&gt;

&lt;p&gt;Create a folder called &lt;code&gt;scripts&lt;/code&gt; in your project root directory and inside it a file with the name of your choice; I called mine &lt;code&gt;update-service-worker.js&lt;/code&gt;. Its content is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const fs = require('fs');
const assetManifest = require('../build/asset-manifest.json');

const urls = Object.values(assetManifest.files).filter(name =&amp;gt; !name.includes('.map'))

fs.readFile('build/service-worker.js', (error, content) =&amp;gt; {

  const newContent = data.replace("%HASHURLS%", JSON.stringify(urls));

  fs.writeFile('build/service-worker.js', newContent, (error) =&amp;gt; {
    error ? console.log(`Error: ${error}`) : console.log(`Success`)
  });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We import &lt;code&gt;fs&lt;/code&gt;, Node’s filesystem module. We also import &lt;code&gt;asset-manifest.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;Object.values()&lt;/code&gt; method on the asset manifests file object (where the generated file names are found) to retrieve an array of hashed filenames. We then filter through them to remove the source map files. You could leave these in and cache them if you wanted, but there’s little reason to.&lt;/p&gt;

&lt;p&gt;Perfect, we’ve got an array of the hashed filenames. You can &lt;code&gt;console.log(urls)&lt;/code&gt; here to verify this if you’d like.&lt;/p&gt;

&lt;p&gt;Now, we simply need to add the hashed filenames to &lt;code&gt;service-worker.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We read the contents of &lt;code&gt;service-worker.js&lt;/code&gt;, then search the contents for &lt;code&gt;%HASHURLS%&lt;/code&gt;, replace it with our hashed filenames and finally write this updated content back to &lt;code&gt;service-worker.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sounds like magic, right?&lt;/p&gt;

&lt;p&gt;Well, that's simply because I haven’t told you about the changes we must make to &lt;code&gt;service-worker.js&lt;/code&gt; to enable the above. Add the following just below where you have defined your &lt;code&gt;STATIC_ASSETS&lt;/code&gt; array.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let CACHE_ASSETS = STATIC_ASSETS.concat(JSON.parse('%HASHURLS%'));

CACHE_ASSETS = new Set(CACHE_ASSETS);

CACHE_ASSETS = Array.from(CACHE_ASSETS);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is almost too simple to explain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We concatenate our &lt;code&gt;STATIC_ASSETS&lt;/code&gt; array with &lt;code&gt;%HASHURLS%&lt;/code&gt; which represents, and will be replaced by, our array of hashed filenames.&lt;/li&gt;
&lt;li&gt;We transform the resulting array into a set to remove duplicates.&lt;/li&gt;
&lt;li&gt;And back into an array.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll also need to change the &lt;code&gt;STATIC_ASSETS&lt;/code&gt; variable name in the install listener to our new array: &lt;code&gt;CACHE_ASSETS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The final &lt;code&gt;service-worker.js&lt;/code&gt; file should resemble the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const CACHE_NAME = "spotlist-cache-v1";

const STATIC_ASSETS = [
  "/index.html",
  "/manifest.json"
]

let CACHE_ASSETS = STATIC_ASSETS.concat(JSON.parse('%HASHURLS%'));

CACHE_ASSETS = new Set(CACHE_ASSETS);

CACHE_ASSETS = Array.from(CACHE_ASSETS);

self.addEventListener('install', event =&amp;gt; {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache =&amp;gt; {
        cache.addAll(CACHE_ASSETS);
      }
    )
  );
});

self.addEventListener('activate', event =&amp;gt; {
  event.waitUntil(
    caches.keys().then(cacheNames =&amp;gt; {
      return Promise.all(
        cacheNames.map(cache =&amp;gt; {
          if (cache !== CACHE_NAME) {
            caches.delete(cache);
          }
        })
      )
    })
  )
})

self.addEventListener('fetch', event =&amp;gt; {
  event.respondWith(
    caches.match(event.request)
      .then(response =&amp;gt; response || fetch(event.request))
  );
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final step is to run the &lt;code&gt;update-service-worker.js&lt;/code&gt; script.&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;build&lt;/code&gt; command of your &lt;code&gt;package.json&lt;/code&gt; file from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"build": "react-scripts build"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"build": "react-scripts build &amp;amp;&amp;amp; npm run update-service-worker"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and add an &lt;code&gt;update-service-worker&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"update-service-worker": "node scripts/update-service-worker.js"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Congratulations&lt;/strong&gt;, you now have an offline compatible PWA without using a single external library.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion:
&lt;/h2&gt;

&lt;p&gt;You don’t need external libraries to take advantage of the exceptional modern features of Service Workers, even when dealing with cache busting hashes in your dynamically generated filenames.&lt;/p&gt;

&lt;p&gt;This is my novel approach to a problem I encountered without any obvious or documented solutions. If you have any suggestions on improvements or alternatives, I’d love to hear them.&lt;/p&gt;

&lt;p&gt;You can find me at &lt;a href="https://www.frankpierce.me/" rel="noopener noreferrer"&gt;frankpierce.me&lt;/a&gt; or email me at &lt;a href="mailto:frank.pierceee@gmail.com"&gt;frank.pierceee@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>productivity</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Understanding Screen</title>
      <dc:creator>Frank Pierce</dc:creator>
      <pubDate>Tue, 17 Jan 2023 04:00:41 +0000</pubDate>
      <link>https://dev.to/llleeeaaannn/understanding-screen-3o1e</link>
      <guid>https://dev.to/llleeeaaannn/understanding-screen-3o1e</guid>
      <description>&lt;p&gt;&lt;em&gt;The following article explains why you may need a tool like Screen and many of the common commands used with the popular terminal multiplexer. If you’re solely looking for the commands please feel free to skip to them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Recently I was attempting to host the Node.js Backend of my Spotlist web application (which you can find it &lt;a href="https://www.spotlist.net/" rel="noopener noreferrer"&gt;here&lt;/a&gt;). Having previously worked with AWS, I decided to host it on an AWS EC2 instance. I created, configured and spun up a Linux-based instance and with little hassle I was able to access my backend API from my client.&lt;/p&gt;

&lt;p&gt;“That was easy”, I thought.&lt;/p&gt;

&lt;p&gt;A couple of hours later I shut down my MacBook and closed all of the programs I had running, including all of the terminals that were open. One of those terminals was connected to my AWS EC2 instance as I had used it to install and run my Spotlist backend.&lt;/p&gt;

&lt;p&gt;When I next tried to continue testing Spotlist, the backend was no longer responding to my API calls. By closing my terminal connected to the EC2 instance I had also terminated everything running on it, specifically my backend. Initially I thought I’d have to leave my terminal open and connected to the instance forever but almost instantly realised that wasn't practically possible. Instead I went in search of tools that would allow an EC2 instance to persist despite the SSH session being disconnected.&lt;/p&gt;

&lt;p&gt;That’s when I found Screen.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Screen (or GNU Screen) is a full-screen window manager that multiplexes a physical terminal between several processes, typically interactive shells.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my own, hopefully more understandable, words: screen lets you create and run unlimited console applications, or consoles, in a single terminal. The reason it works for our particular issue is because programs on a screen session will continue to run even when the window is not visible and also when the whole screen session is detached from the users terminal.&lt;/p&gt;

&lt;p&gt;This will allow me to create a screen on my EC2 instance, run my backend and close my terminal connected to the instance while knowing that my backend (or any other programs I chose to run) will continue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Screen is installed on all major Linux distributions by default. You can verify this by running the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For full disclosure, my AWS instance is running version 4.01.&lt;/p&gt;

&lt;p&gt;If screen is not installed on your device, you can run execute the following command to install it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt install screen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a screen
&lt;/h2&gt;

&lt;p&gt;Starting a new screen session is as simple as typing the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now begin using this virtual terminal or screen and in my case, I simply ran my backend here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding screens
&lt;/h2&gt;

&lt;p&gt;We can theoretically create endless screens and therefore to view a list of all open screen sessions, we can use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -ls
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created several screens and ran the above command, here is the output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflrp2rmj4dmv2pasbxcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fflrp2rmj4dmv2pasbxcm.png" alt="Sample output for 'screen -ls' command" width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The output tells us that there are 6 screen sessions. It gives us the name of each screen session and indicates whether it is attached or detached.&lt;/p&gt;

&lt;p&gt;As the name indicates, attached means some process (for example your terminal) is using that screen session. On the other hand, detached means someone left the screen session running (and therefore didn’t terminate or kill it) but disconnected from it. A screen session being detached does not mean it is terminated or that any programs running on it are terminated, the programs will still run and the screen can be reattached to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attaching and detaching
&lt;/h2&gt;

&lt;p&gt;If you open a terminal window, let's say in an AWS instance, and type screen to create a new screen as mentioned above you will be automatically attached to this screen. You can then run your desired program in the screen and when you want to disconnect from the screen (to close the terminal or open another screen session) you execute the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, this does not kill the screen session or any programs running on it.&lt;/p&gt;

&lt;p&gt;To re-attach to a screen session, you have a few options.&lt;/p&gt;

&lt;p&gt;If you only have a single screen session running (but detached) you can attach to it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -r
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have multiple screen sessions running as below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9p7grbmyfsyz5zpou6sc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9p7grbmyfsyz5zpou6sc.jpg" alt="Screenshot showing multiple screens" width="525" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To attach to a particular one you will need to execute the following command (where ‘9929.pts-0.ip-172–31–88–159’ is the screen name):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -r 9929.pts-0.ip-172–31–88–159
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, you don’t need to be this precise and can simply use the first character of the screen name as long as no other screen shares the same first character (in which case you can use the first two…). Therefore the below command would have the same effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -r 9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if you want to use a screen session as quickly as possible? The following command will connect to the first screen session available or create a new one if none exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -x -R
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Naming a screen
&lt;/h2&gt;

&lt;p&gt;Now you may like a name like ‘9929.pts-0.ip-172–31–88–159’, but I prefer a more colloquial name. Luckily Screen GNU allows us to name our screens. The below creates a screen with a custom name, in this case, ‘MyNickName’:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;screen -S MyNickName
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Terminating screens
&lt;/h2&gt;

&lt;p&gt;If you ever need to terminate (or kill) all screen sessions, use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;killall screen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Want more?
&lt;/h2&gt;

&lt;p&gt;This article only covers the basics of Screen and it’s most commonly used screen commands. For more commands please visit &lt;a href="https://www.gnu.org/software/screen/" rel="noopener noreferrer"&gt;https://www.gnu.org/software/screen/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you'd like to get in touch, can find me at &lt;a href="https://www.frankpierce.me/" rel="noopener noreferrer"&gt;frankpierce.me&lt;/a&gt; or email me at &lt;a href="mailto:frank.pierceee@gmail.com"&gt;frank.pierceee@gmail.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
