<?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: Christian Ascone</title>
    <description>The latest articles on DEV Community by Christian Ascone (@christianascone).</description>
    <link>https://dev.to/christianascone</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%2F1146108%2F4f52ac26-421f-4581-aeba-ccb047cb405c.jpeg</url>
      <title>DEV Community: Christian Ascone</title>
      <link>https://dev.to/christianascone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/christianascone"/>
    <language>en</language>
    <item>
      <title>Syncing dotfiles across PCs with GNU Stow</title>
      <dc:creator>Christian Ascone</dc:creator>
      <pubDate>Sat, 01 Mar 2025 16:39:49 +0000</pubDate>
      <link>https://dev.to/christianascone/syncing-dotfiles-across-pcs-with-gnu-stow-5b3k</link>
      <guid>https://dev.to/christianascone/syncing-dotfiles-across-pcs-with-gnu-stow-5b3k</guid>
      <description>&lt;p&gt;Whether I’m on my old laptop, my shiny work machine, or even a random PC I’ve borrowed for a weekend, I want my tools, shortcuts, and configs to feel like home.&lt;/p&gt;

&lt;p&gt;Bash, vim, terminal emulators, tmux, all those things that make your system yours.&lt;/p&gt;

&lt;p&gt;Copying them over manually, dealing with version mismatches or forgetting where I stashed the latest tweaks? Yeah, that was a mess.&lt;/p&gt;

&lt;h1&gt;
  
  
  What’s Stow, anyway?
&lt;/h1&gt;

&lt;p&gt;So, what’s this Stow that saved me from my dotfile nightmare? At its core, &lt;a href="https://www.gnu.org/software/stow/" rel="noopener noreferrer"&gt;Stow is a tool for Linux&lt;/a&gt; that helps you manage files using symbolic links; think of them as shortcuts that point to where your actual files live.&lt;/p&gt;

&lt;p&gt;"Symbolic links" might sound fancy or intimidating if you’re not familiar with them, but it’s really just a way to keep things tidy without copying files all over the place.&lt;/p&gt;

&lt;p&gt;Instead of scattering my .bashrc, .vimrc, and other dotfiles across every machine I use, I keep them all in one neat folder. Then, Stow steps in and “links” them to the right places, like my home directory, so everything works as if I’d put them there myself. No more duplicating files or wondering which version is the latest.&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%2Ff0zyjuceusn3twimksqb.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%2Ff0zyjuceusn3twimksqb.png" alt="Stow schema" width="800" height="220"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, here is my configuration structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.
├── ghostty
│   └── .config
│       └── ghostty
│           └── config
├── ideavimrc
│   └── .ideavimrc
├── vimrc
│   └── .vimrc
├── bashrc
│   └── .bashrc
├── kitty
│   └── .config
│       └── kitty
│           └── kitty.conf
├── tmux
│   └── .config
│       └── tmux
│           └── tmux.conf
└── zsh
    └── .zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every folder in there is for a tool I use, like Kitty or Zsh, and I can put in one file, a couple of files, or even a whole subfolder. Stow grabs whatever’s inside and makes it work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting started with Stow
&lt;/h1&gt;

&lt;p&gt;Okay, so how do you actually use Stow to make this magic happen? First, you need Stow on your machine.&lt;/p&gt;

&lt;p&gt;On most Linux systems it’s a quick install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#Debian/Ubuntu&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;stow
&lt;span class="c"&gt;#Fedora&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dnf &lt;span class="nb"&gt;install &lt;/span&gt;stow
&lt;span class="c"&gt;#Arch Linux&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; stow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On macOS it’s available with &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;brew&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;stow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it’s there, you’re ready to roll.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up my dotfiles folder
&lt;/h2&gt;

&lt;p&gt;Before Stow can work, you’ll need to organize your dotfiles. Start by creating a folder called &lt;code&gt;dotfiles&lt;/code&gt; (I assume to place it in home directory, so the full path is &lt;code&gt;~/dotfiles&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Next, create a structure similar to the one shown above, with a separate folder for each tool. Ensure the file structure adheres to the following rule:&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%2Fp2ukaet8axxyjw48oelu.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%2Fp2ukaet8axxyjw48oelu.png" alt="Stow files path explanation" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Main directory (in this case dotfiles) is just the name of the container folder&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The subfolder should be named after the tool. While it's best to use the tool's actual name for clarity, you can choose any name to distinguish it. For example, you could name it &lt;code&gt;foobar&lt;/code&gt; and Stow will still work correctly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, the rest is the structure of your config files&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you set it up you can cd into dotfiles directory and run stow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;dotfiles
stow kitty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stow will locate the &lt;code&gt;dotfiles/kitty&lt;/code&gt; directory, take its contents, and create symlinks in your home directory while preserving the same structure. For example, &lt;code&gt;~/dotfiles/kitty/.config/kitty/config&lt;/code&gt; will be symlinked as &lt;code&gt;~/.config/kitty/config&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;.config&lt;/code&gt; folder doesn’t exist in my home directory, Stow will create it automatically. Pretty neat, right?&lt;/p&gt;

&lt;p&gt;It might take a bit of time to get everything sorted, but once it’s done, you’ll have all your configs in one place, ready for Stow to link up. And trust me, it feels great to bring some order to that chaos!&lt;/p&gt;

&lt;p&gt;Need to link all your configurations at once? No problem, Stow supports wildcard patterns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;stow &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="c"&gt;# Create symlinks for every tool&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it!&lt;/p&gt;

&lt;h1&gt;
  
  
  Managing your configs: Deleting and Adopting
&lt;/h1&gt;

&lt;p&gt;So, you’ve got your dotfiles linked up with Stow, and everything’s running smoothly. But what if you want to undo one of those links, like, say, you’re done with your old Kitty setup and want to clean it out? Or maybe you’ve tweaked a config directly in your home directory and want Stow to take it over? No worries, Stow’s got you covered for both.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deleting a linked configuration
&lt;/h2&gt;

&lt;p&gt;To remove those symlinks, Stow offers a handy delete option. Just head to your &lt;code&gt;~/dotfiles&lt;/code&gt; folder in the terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;stow &lt;span class="nt"&gt;-D&lt;/span&gt; kitty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-D&lt;/code&gt; flag tells Stow to delete the symlinks it created for the kitty folder. It will unlink &lt;code&gt;~/.config/kitty/kitty.conf&lt;/code&gt; (or whatever’s in there) without touching the original files in &lt;code&gt;~/dotfiles/kitty&lt;/code&gt;. Those stay safe in your dotfiles folder, so you can always bring them back later with a quick &lt;code&gt;stow kitty&lt;/code&gt; if you change your mind.&lt;/p&gt;

&lt;p&gt;It’s like hitting an undo button, simple and stress-free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adopting existing configs
&lt;/h2&gt;

&lt;p&gt;Now, here’s a cool trick Stow can do: adopting configs you’ve already got in your home directory.&lt;/p&gt;

&lt;p&gt;Picture this: you’ve been tweaking your &lt;code&gt;.zshrc&lt;/code&gt; right in &lt;code&gt;~/.zshrc&lt;/code&gt;, but now you want it to live in your &lt;code&gt;~/dotfiles/zsh&lt;/code&gt; folder under Stow’s control. Instead of moving it manually, Stow can “adopt” it for you.&lt;/p&gt;

&lt;p&gt;Here’s how it works. First, make sure your zsh folder exists in ~/dotfiles/ with the right structure, like &lt;code&gt;~/dotfiles/zsh/.zshrc&lt;/code&gt;, but leave it empty or with an older version. Then, from your &lt;code&gt;~/dotfiles&lt;/code&gt; directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;stow &lt;span class="nt"&gt;--adopt&lt;/span&gt; zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens next is pretty slick. Stow sees the existing &lt;code&gt;~/.zshrc&lt;/code&gt; in your home directory, grabs it, and moves it into &lt;code&gt;~/dotfiles/zsh/.zshrc&lt;/code&gt;, replacing whatever was there. Then it creates a symlink back to &lt;code&gt;~/.zshrc&lt;/code&gt;, ensuring that everything continues to work as before.&lt;/p&gt;

&lt;p&gt;It’s like Stow saying, 'Hey, I’ll take this off your hands and keep it organized for you'. Just be careful: if you already have a different &lt;code&gt;.zshrc&lt;/code&gt; in &lt;code&gt;~/dotfiles/zsh&lt;/code&gt; already, it will get overwritten, so maybe back it up first if you’re not sure.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrapping it up
&lt;/h1&gt;

&lt;p&gt;Stow takes the chaos of managing dotfiles and turns it into a simple, structured process, maybe even a little fun.&lt;/p&gt;

&lt;p&gt;No more scrambling to copy configs between machines. No more version mismatches or lost tweaks. Just a clean, consistent setup that follows you wherever you go.&lt;/p&gt;

&lt;p&gt;Setting up the structure takes a bit of effort upfront, but once it’s in place, you’ll wonder how you ever managed without it.&lt;/p&gt;

</description>
      <category>gnustow</category>
      <category>linux</category>
      <category>dotfiles</category>
      <category>organizingfiles</category>
    </item>
    <item>
      <title>Laravel + React project with Authentication &amp; User Panel in less than a minute</title>
      <dc:creator>Christian Ascone</dc:creator>
      <pubDate>Mon, 14 Oct 2024 16:57:13 +0000</pubDate>
      <link>https://dev.to/christianascone/laravel-react-project-with-authentication-user-panel-in-less-than-a-minute-4cbm</link>
      <guid>https://dev.to/christianascone/laravel-react-project-with-authentication-user-panel-in-less-than-a-minute-4cbm</guid>
      <description>&lt;p&gt;I recently posted a &lt;a href="https://x.com/christianascone/status/1844300255296110890" rel="noopener noreferrer"&gt;video on &lt;strong&gt;X&lt;/strong&gt;&lt;/a&gt; where I set up a &lt;strong&gt;Laravel + React&lt;/strong&gt; project with authentication and a user profile page in less than a minute! I thought it would be helpful to share a written version for anyone who prefers following along with a detailed guide.&lt;/p&gt;

&lt;p&gt;In this guide, I'll show you how to quickly set up a new &lt;strong&gt;Laravel&lt;/strong&gt; project with a &lt;strong&gt;React&lt;/strong&gt; frontend and built-in authentication, all using &lt;strong&gt;Laravel Breeze&lt;/strong&gt;. We'll have a functional user panel with profile editing in just a few steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Laravel?
&lt;/h2&gt;

&lt;p&gt;Laravel is a popular PHP framework used for building modern web applications. It's gaining traction quickly, in fact, in September 2024, &lt;a href="https://blog.laravel.com/accel-invests-57m-into-laravel" rel="noopener noreferrer"&gt;&lt;strong&gt;Accel&lt;/strong&gt; announced a $57 million investment in Laravel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;According to the official site, Laravel is "The PHP Framework for Web Artisans," and it truly lives up to that description.&lt;/p&gt;

&lt;p&gt;I won’t spend too much time on what Laravel is, there's a ton of information available on its &lt;a href="https://laravel.com" rel="noopener noreferrer"&gt;official website&lt;/a&gt; and &lt;a href="https://laravel.com/docs" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. Instead, let's get right to setting up your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;p&gt;We’ll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Laravel&lt;/strong&gt; (for backend APIs)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SQLite&lt;/strong&gt; which is the default database for Laravel projects (though you can switch to another database easily)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;React&lt;/strong&gt; for the frontend (with Inertia.js)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Laravel Breeze&lt;/strong&gt; to handle authentication and user management&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;To follow this guide, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;PHP 8 (preferably 8.3)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Composer&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Node.js (v21)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready? Let's get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a New Laravel Project
&lt;/h3&gt;

&lt;p&gt;First, use Composer to create a new Laravel project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer create-project laravel/laravel laravel-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new directory with the default Laravel setup. Move into the project directory and serve the application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;laravel-project
php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you visit &lt;a href="http://localhost:8000" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:8000&lt;/code&gt;&lt;/a&gt;, you'll see the Laravel welcome page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8hlrgjfunmjb5abpkkhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8hlrgjfunmjb5abpkkhl.png" alt="Laravel project home" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations, you’ve created your first Laravel project! ✅&lt;/p&gt;

&lt;p&gt;By default, Laravel uses an SQLite database, which has already been set up for you. You can find the database file at &lt;code&gt;database/database.sqlite&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Install Laravel Breeze
&lt;/h3&gt;

&lt;p&gt;Next, we'll add &lt;a href="https://github.com/laravel/breeze" rel="noopener noreferrer"&gt;&lt;strong&gt;Laravel Breeze&lt;/strong&gt;&lt;/a&gt;, which provides a minimal implementation of authentication, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Login&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Registration&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Password Reset&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Email Verification&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Password Confirmation&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, Breeze includes a simple profile page where users can update their information.&lt;/p&gt;

&lt;p&gt;Add Breeze package by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require laravel/breeze &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, install Breeze into your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan breeze:install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.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%2Fb7vuf5iqu68gbbfrgfol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fb7vuf5iqu68gbbfrgfol.png" alt="Laravel Breeze installation" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;During installation, Breeze will prompt you for a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I choose &lt;strong&gt;React&lt;/strong&gt; for the frontend, but there are a few alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blade&lt;/li&gt;
&lt;li&gt;Livewire&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;API only&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Optionally, you can enable &lt;strong&gt;Dark mode&lt;/strong&gt;, &lt;strong&gt;Server-Side Rendering (SSR)&lt;/strong&gt;, &lt;strong&gt;Typescript&lt;/strong&gt;, and &lt;strong&gt;ESLint&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;When asked about a testing framework, you can choose between &lt;strong&gt;Pest&lt;/strong&gt; or &lt;strong&gt;PHPUnit&lt;/strong&gt;. I'll be using &lt;strong&gt;Pest&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Once the process is complete, Breeze will have added everything you need for authentication and user management.&lt;/p&gt;

&lt;p&gt;It's done! 🎉&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Run Your Application
&lt;/h3&gt;

&lt;p&gt;Now that Breeze is set up your application is ready and you can run it again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="http://localhost:8000/register" rel="noopener noreferrer"&gt;&lt;code&gt;http://localhost:8000/register&lt;/code&gt;&lt;/a&gt; to create a new user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F2sezw6zsfqgqe53qgit4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F2sezw6zsfqgqe53qgit4.png" alt="User registration" width="800" height="788"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You'll start with an empty dashboard and can navigate to your profile page to edit your user details and update your password.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5jsvy6ndmurtsud6rzqs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5jsvy6ndmurtsud6rzqs.png" alt="User profile page" width="800" height="785"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The data are already stored in your SQLite database, and your app is essentially ready to be deployed!&lt;/p&gt;

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

&lt;p&gt;By following these steps, you've successfully bootstrapped a full-stack web application using &lt;strong&gt;Laravel&lt;/strong&gt; and &lt;strong&gt;React&lt;/strong&gt;, complete with authentication and a user profile management system.&lt;/p&gt;

&lt;p&gt;Breeze gives you a great starting point, but from here you can focus on adding custom business logic, improving UI/UX, or integrating third-party services, all while standing on the shoulders of Laravel’s ecosystem.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>react</category>
      <category>node</category>
    </item>
    <item>
      <title>I switched from Notion to Obsidian</title>
      <dc:creator>Christian Ascone</dc:creator>
      <pubDate>Tue, 23 Apr 2024 10:06:30 +0000</pubDate>
      <link>https://dev.to/christianascone/i-switched-from-notion-to-obsidian-4p3g</link>
      <guid>https://dev.to/christianascone/i-switched-from-notion-to-obsidian-4p3g</guid>
      <description>&lt;p&gt;One of the funniest and most annoying (yes) thing is possible to do nowadays is to try many different tools with the exact same purpose. Need to write a document? Google Docs, Notion, Word, Libreoffice, Coda, Apple notes and so on.&lt;/p&gt;

&lt;p&gt;Usually even if the purpose is the same what obviously change are the features provided by any of them and one of the biggest mistake anyone can make is to seek the perfect tool.&lt;/p&gt;

&lt;p&gt;Spoiler: the perfect tool does not exist. And I can hear you saying: "What an obvious thing you're stating! But if I choose a tool with a lot of features, surely I can find what I need and ignore the rest."&lt;/p&gt;

&lt;p&gt;And that would be the &lt;strong&gt;real&lt;/strong&gt; mistake.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work in the present
&lt;/h2&gt;

&lt;p&gt;Trying many tools is funny, but as mentioned it can also become quite annoying and frustrating. You spend time in something that you might later abandon, all while continuously chasing after perfection.&lt;/p&gt;

&lt;p&gt;My perfection had a name: &lt;a href="http://notion.so"&gt;Notion&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Notion is a versatile workspace that offers a set of features for both solo work and team collaboration. It includes blank pages, databases, interconnected data and a wide array of ready-to-use templates.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fep9972en0177s9l8u0at.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fep9972en0177s9l8u0at.png" alt="Notion homepage screenshot" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Honestly I think it's an &lt;strong&gt;amazing&lt;/strong&gt; tool. Amazing, really. Once you learn how to use it it's pretty easy and straightforward because every page is basically a markdown file (except for databases) so you must not deal with complicated logic behind the scene.&lt;/p&gt;

&lt;p&gt;But in my case there was a problem: &lt;strong&gt;too much noise&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I worked with Notion throughout 2023 and the beginning of 2024 using some stuff always saying: "it's powerful, I'm not using many features but I could in the future" and I don't know if that future will ever come.&lt;/p&gt;

&lt;p&gt;I was basically using it as a cauldron for ideas or notes, something that could be done with a paper notebook. That was (and is) my present.&lt;/p&gt;

&lt;h2&gt;
  
  
  Control, not power
&lt;/h2&gt;

&lt;p&gt;Power was useless for my personal use case.&lt;/p&gt;

&lt;p&gt;I use Notion in team, but the problem lies with my use case and data. All that power was wasted, and meanwhile I was overcomplicating my life for a hypothetical future.&lt;/p&gt;

&lt;p&gt;The solution was already installed on both my computer and my phone: &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ob90hjh0h6j17y5mbgx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ob90hjh0h6j17y5mbgx.png" alt="Obsidian homepage" width="800" height="260"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For those who don't know it, Obsidian is ssentially a straightforward writing app (similar to the ones I mentioned earlier) that utilizes &lt;strong&gt;markdown&lt;/strong&gt; as its file format.&lt;/p&gt;

&lt;p&gt;Obsidian is not the focus of this article; rather, it’s the thought process that led me to it, but I need to cover a few topics to explain what Obsidian does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Markdown is an open and widely spread format. No more "docx to pdf" conversion or formatting mess while copying content away.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Markdown is a markup language. Any file is human readable, there's no binary content. This makes its management really easy (you can use git for versioning for example).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Obsidian workspace let you connect documents. While your workspace grows you create a map of your files.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Obsidian features &lt;strong&gt;can&lt;/strong&gt; be extended using plugins. If you don't need them you can use the "vanilla version".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Obsidian workspace is just a folder with files and subfolders. Nothing more. You can browse it with your file manager, edit them with your preferred editor, reopen Obsidian and you will see the changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;My two requirements were the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Independence (and consequently simplicity)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connections among notes&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notion is strong on the second point, but not on the first one. It's bloated by default and you don't have direct access to the files; hence, you're strongly connected to it.&lt;/p&gt;

&lt;p&gt;Obsidian, on the other hand, provides the same type of connection but complete independence. Folder and files are stored directly on the filesystem, allowing synchronization with cloud storage services and compatibility with any text editor.&lt;/p&gt;

&lt;p&gt;With this goal in mind, I needed to figure out how to efficiently transfer a substantial amount of content from Notion to Obsidian. Fortunately, both platforms offer dedicated options for export and import: &lt;a href="https://help.obsidian.md/import/notion"&gt;https://help.obsidian.md/import/notion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned, &lt;strong&gt;Notion’s features are more complex&lt;/strong&gt; than Obsidian’s, so some notes may appear differently. To replicate certain functionality, you’ll need to find a suitable plugin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sync
&lt;/h2&gt;

&lt;p&gt;The big drawback, if we can call it that, is that Obsidian is not cloud by default, because we have seen that it's just a tool able to open a directory of markdown files.&lt;/p&gt;

&lt;p&gt;However it is still possible to sync our workspaces in order to enter our vaults from many devices:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://obsidian.md/sync"&gt;Obsidian Sync&lt;/a&gt;: This is the paid plan and it offers a set of fancy features.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Homemade sync: Simply synchronize the workspace folder with your cloud storage.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my opinion what's more interesting about the paid plan is the "Shared vaults" feature which let you collaborate with your team in real-time, but as I already said this is for my personal workspace so I decided to build my homemade sync solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ficd317uj92xrs1e1hres.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ficd317uj92xrs1e1hres.png" alt="My actual Obsidian setup with backup" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The workspace is stored on Google Drive which is mounted on my computers allowing me to open it as if it were on my hard drive.&lt;/p&gt;

&lt;p&gt;Finally I also periodically backup it on git (Obsidian has a plugin for that too) so I keep the total control of my data.&lt;/p&gt;

&lt;p&gt;In conclusion, I don’t miss Notion because I haven’t lost any features I was using before. However, this assessment is based on my specific needs. My advice: don’t be afraid to experiment and embrace tools that meet your current requirements—even if other options offer more functionalities that you might use later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Focus on what you need, the perfect tool does not exist, but the perfect tool for the current you does.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>notion</category>
      <category>obsidian</category>
      <category>notes</category>
      <category>notesapp</category>
    </item>
    <item>
      <title>Lazy loading React components</title>
      <dc:creator>Christian Ascone</dc:creator>
      <pubDate>Wed, 10 Apr 2024 16:06:48 +0000</pubDate>
      <link>https://dev.to/christianascone/lazy-loading-react-components-5d6f</link>
      <guid>https://dev.to/christianascone/lazy-loading-react-components-5d6f</guid>
      <description>&lt;p&gt;Over the life of an application, performance optimization can play a crucial role in its success or failure and I recently encountered an issue that severely impacted the user experience.&lt;/p&gt;

&lt;p&gt;Providing some context, my requirement was to show a list of N items using React. Unfortunately, I couldn’t use infinite scrolling, and each item needed to fetch its own data via separate HTTP calls.&lt;/p&gt;

&lt;p&gt;Initially, I successfully implemented a component able to fetch data during initialization, nothing difficult, but performance was really bad when there were many items on screen and as previously mentioned I could not use infinite scrolling or pagination due to backend limitations, leaving frontend adjustments as the sole option.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setup
&lt;/h1&gt;

&lt;p&gt;Let’s walk through a sample project that illustrates the problem and demonstrates a solution.&lt;/p&gt;

&lt;p&gt;I chose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Javascript: To avoid compiling so we focus on our goal. This same approach works with typescript as well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://jsonplaceholder.typicode.com/posts"&gt;https://jsonplaceholder.typicode.com&lt;/a&gt;: For fetching sample data. It's a "Free fake and reliable API for testing and prototyping."&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vitejs.dev"&gt;Vite&lt;/a&gt;: Recommended after create-react-app deprecation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;npm: So that everyone can feel comfortable.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll find the link to the complete project at the end of this post, but let’s cover all the steps here.&lt;/p&gt;

&lt;p&gt;Our goal is to initiate the (potentially) resource-intensive fetch only when the component becomes visible to the user, otherwise we would be performing unnecessary work that could compromise the overall performance.&lt;/p&gt;

&lt;p&gt;Let's start creating a new react project using Vite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ npm create vite@latest
✔ Project name: … react-render-visible
✔ Select a framework: › React
✔ Select a variant: › JavaScript

Scaffolding project &lt;span class="k"&gt;in &lt;/span&gt;react-render-visible...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we need a very simple component that fetches data from APIs and renders it in DOM. This is how it initially looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NinjaComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// fetch data&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Show data */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;NinjaComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's add some logic retrieving fake data from &lt;a href="https://jsonplaceholder.typicode.com/posts"&gt;https://jsonplaceholder.typicode.com&lt;/a&gt;. It offers a set of basic CRUD operations hence we are going to implement a GET by id provided as input parameter of the component.&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://jsonplaceholder.typicode.com/posts/1"&gt;https://jsonplaceholder.typicode.com/photos/1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1) Add `id` parameter&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NinjaComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2) Fetch data using `id` in url and update `data`&lt;/span&gt;
    &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/photos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

&lt;span class="c1"&gt;// 3) If `data` is set we render id, title and an image, else it shows a loading text&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;&lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1px dashed yellow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thumbnailUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add it to our home page to check how it works. Let's make the necessary edits in &lt;code&gt;App.jsx&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NinjaComponent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/NinjaComponent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Ding&lt;/span&gt; &lt;span class="nx"&gt;Dong&lt;/span&gt; &lt;span class="nx"&gt;Bug&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NinjaComponent&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the initial setup complete, we’ve built our data component and integrated it into the homepage to display sample data. Now, let’s address a performance concern.&lt;br&gt;&lt;br&gt;
Time to start the project: &lt;code&gt;npm run dev&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffgh4say69m9vgbna5906.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffgh4say69m9vgbna5906.png" alt="Project startup" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
  
  
  Good, but I've got 1000 elements!
&lt;/h1&gt;

&lt;p&gt;While the NinjaComponent works well, it’s not exactly stealthy. What if we need to render a ton of that?&lt;/p&gt;

&lt;p&gt;Try updating the &lt;code&gt;App.jsx&lt;/code&gt; with 500 or 1000 instances of NinjaComponent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NinjaComponent&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After relaunching the project, check the console:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4a817wzo3kmt1g1c0m8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4a817wzo3kmt1g1c0m8.png" alt="Requests and resources with 1000 elements" width="800" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is an insane amount of wasted resources especially if you are not "using" all those components because only the first &lt;strong&gt;one or two&lt;/strong&gt; are on screen.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1000 requests for fetching data (&lt;a href="https://react.dev/reference/react/StrictMode#fixing-bugs-found-by-double-rendering-in-development"&gt;x2 because we have StrictMode in dev&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;1000 images to download&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Solution: IntersectionObserver
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0sm2sr09p46rpo7p07j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd0sm2sr09p46rpo7p07j.png" alt="Screen boundaries and elements" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The green area represents what the user actually sees on the screen, while the red area includes elements already in DOM, but currently off-screen.&lt;/p&gt;

&lt;p&gt;To manage on-screen visibility, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API"&gt;IntersectionObserver&lt;/a&gt; API comes to our aid. It "provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport" so basically it can determine exactly what we need.&lt;/p&gt;

&lt;p&gt;More in detail we will use two methods provided by IntersectionObserver:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;observe()&lt;/code&gt; method is like telling the IntersectionObserver to start keeping an eye on a specific element. When the element’s visibility changes in relation to the viewport or a specified ancestor element, the IntersectionObserver will take note and execute its callback function. This function will receive an array of IntersectionObserverEntry objects, each representing a visibility change event.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;disconnect()&lt;/code&gt; method tells the IntersectionObserver to stop watching all of its target elements. It’s like saying, “You can rest now, no need to track any more changes.” This is particularly useful when you no longer need to monitor the visibility changes of the target elements.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this in mind it is possible to proceed creating a custom hook that will be integrated in NinjaComponent so that it knows when fetch can start effectively filling data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useFetchOnVisible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// the state to store the fetched data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// the state to indicate the loading status&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// create a new intersection observer&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;IntersectionObserver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// get the first entry&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="c1"&gt;// if the entry is visible and not loading (and we still don't have data)&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isIntersecting&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// set the loading state to true&lt;/span&gt;
          &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

          &lt;span class="c1"&gt;// call the fetch function and set the data state&lt;/span&gt;
          &lt;span class="nf"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// the ratio of the element's area that is visible&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// observe the ref element&lt;/span&gt;
    &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// unobserve the ref element when the component unmounts&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchFn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// the dependencies of the effect&lt;/span&gt;

  &lt;span class="c1"&gt;// return the data and loading state&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;useFetchOnVisible&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#threshold"&gt;&lt;code&gt;threshold&lt;/code&gt;&lt;/a&gt; in IntersectionObserver is like a visibility marker for your target element. It tells the IntersectionObserver when to notify you about the visibility changes of the target element.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;threshold&lt;/code&gt; of 0.0 means that even a single visible pixel counts as the target being visible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A &lt;code&gt;threshold&lt;/code&gt; of 1.0 means that the entire target element must be visible for the callback to be invoked.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With hook available we just need to update the NinjaComponent behaviour moving the fetch function inside the hook itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useFetchOnVisible&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../hooks/useFetchOnVisible&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NinjaComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// `ref` places a target on object to track &lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// fetch function has been moved out of the useEffect hook.&lt;/span&gt;
  &lt;span class="c1"&gt;// it just fetches data and parse json.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/photos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// replace with your API endpoint&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="c1"&gt;// !!! use the custom hook with `ref` and fetch function&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFetchOnVisible&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getSource&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// UI does not change, it just add the `ref` tag in order to track div&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1px dashed yellow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;thumbnailUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;NinjaComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ready to test?&lt;/p&gt;

&lt;h2&gt;
  
  
  Lazy loading in action
&lt;/h2&gt;

&lt;p&gt;Assuming everything is properly configured, let’s open the page and observe what happens in the console. We’ll pay close attention to loading times and downloaded resources.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfjaoav9d8trmpm8ndei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxfjaoav9d8trmpm8ndei.png" alt="Request and resources with lazy loading" width="800" height="42"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The page opening now requires few requests, consumes fewer resources, and the total loading time has dramatically improved — from around 10 seconds to a mere 0.5 seconds!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo2th6liigcc6e9sb73q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo2th6liigcc6e9sb73q.gif" alt="Lazy loading while scrolling" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we scroll down the page, the &lt;strong&gt;IntersectionObserver&lt;/strong&gt; detects the moment when a component becomes visible and triggers the fetch function to retrieve data as needed.&lt;/p&gt;

&lt;p&gt;Even increasing the number of components to 2000, the request count at startup remains unchanged and performance does not suffer. All thanks to fetching data precisely when it’s required, rather than preemptively! 🎉&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/christianascone/DingDongBug-Code-React-Render-Visible"&gt;GitHub: Source code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>lazyloading</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Committing to Clarity</title>
      <dc:creator>Christian Ascone</dc:creator>
      <pubDate>Wed, 27 Dec 2023 07:45:09 +0000</pubDate>
      <link>https://dev.to/christianascone/committing-to-clarity-2oh</link>
      <guid>https://dev.to/christianascone/committing-to-clarity-2oh</guid>
      <description>&lt;p&gt;Nowadays every developer should know Git but in case you don't (listen to me, go studying that and you will save a lot of headaches) here is a brief explanation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Git is a distributed version control system that allows multiple developers to collaborate on a project simultaneously without overwriting each other's work. It keeps track of changes made to files and allows users to revert to previous versions, branch out to work on new features, and merge changes back together.&lt;/p&gt;

&lt;p&gt;The git commit message is a way to document and describe the changes being made to a file or a set of files in a Git repository. It is entered by the developer as part of the commit process and serves as a summary of the changes made.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And "Commit message" itself is the protagonist of this article.&lt;/p&gt;

&lt;h1&gt;
  
  
  "Who cares"
&lt;/h1&gt;

&lt;p&gt;"What should I write as commit message?"&lt;br&gt;&lt;br&gt;
"Who cares"&lt;/p&gt;

&lt;p&gt;This is what I heard many times when someone needed to write a message for a commit, with the consequence that after a few commits the repository was unreadable and unintelligible.&lt;/p&gt;

&lt;p&gt;In small teams or individual projects, this is not a problem, however, a git repository represents the history of our code: what's been added, removed, edited, which features we merged and which ones are still in progress.&lt;/p&gt;

&lt;p&gt;Therefore writing a good commit message should be a strict rule, even when we are working on individual projects, otherwise we are going to miss pieces of information and we will be forced to rely only on our memory.&lt;/p&gt;

&lt;p&gt;That said, another big issue is how to standardize messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 4 different commit messages for the same&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Add new String utils class
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; new String utils class
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; New class: String utils
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Added String utils
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I edited these a bit to hide confidential information, but they are real commit messages from different people working on the same repository and as you can see the style is always different.&lt;/p&gt;

&lt;p&gt;This introduces a few issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It's time-consuming&lt;/strong&gt;: every time you have to think about how to format your message (What verbal tense do I use? Lowercase or not?).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Heterogeneous history&lt;/strong&gt;: as seen above the history is not linear due to different styles (even the same person could adopt several styles at various moments).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwf5d8ep0stru1cp2otg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxwf5d8ep0stru1cp2otg.png" alt="confused woman meme" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hence, what is needed is a standardized way of building a commit message which can be shared among people, allowing us to write something as similar as possible to other commits.&lt;/p&gt;

&lt;h1&gt;
  
  
  Solution: Conventional Commits
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;The Conventional Commits specification is a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.conventionalcommits.org/en/v1.0.0/"&gt;Conventional Commits 1.0.0&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Since I discovered Conventional Commits my approach to commits changed and improved drastically. Its specification provides practical "tools" (nothing magical, but very powerful) which help developers to use git smarter and easier.&lt;/p&gt;

&lt;p&gt;First, let's see the structure of a message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;[&lt;/span&gt;optional scope]: &amp;lt;description&amp;gt;

&lt;span class="o"&gt;[&lt;/span&gt;optional body]

&lt;span class="o"&gt;[&lt;/span&gt;optional footer&lt;span class="o"&gt;(&lt;/span&gt;s&lt;span class="o"&gt;)]&lt;/span&gt;

&lt;span class="c"&gt;# Or simply&lt;/span&gt;

&amp;lt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;: &amp;lt;description&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So easy? Yes. The magic lies beneath the &lt;em&gt;type&lt;/em&gt; argument.&lt;br&gt;&lt;br&gt;
It describes the scope and context of a commit quickly and easily:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;fix&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;feat&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;docs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;refactor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;test&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are just a few examples, but all of them immediately give a hint to readers about what has been added or edited, even without knowing anything about Conventional Commits.&lt;/p&gt;

&lt;p&gt;As stated in official website it is inspired by, and based heavily on, the &lt;a href="https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines"&gt;Angular Commit Guidelines&lt;/a&gt;, which include other nice features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb12ck4l75sk770llnrr6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb12ck4l75sk770llnrr6.png" alt="Angular commit history" width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Angular commit history looks so clean and easy to read.&lt;/p&gt;

&lt;p&gt;Moreover, with this standard it is possible to automate changelogs generation and and semantic version bump (check &lt;a href="https://semver.org"&gt;SemVer&lt;/a&gt;) while keeping git history consistent: it's a win win.&lt;/p&gt;

&lt;h1&gt;
  
  
  Go try it out
&lt;/h1&gt;

&lt;p&gt;In conclusion, Conventional Commits is not something you install, it's a set of rules that a team (or an individual) can adopt in its git workflow without any cost, enhancing collaboration within your development team.&lt;/p&gt;

&lt;p&gt;By adopting this structured approach to commit messages, you can improve communication, simplify code reviews, automate release notes generation, and ultimately build a more organized and efficient development process.&lt;/p&gt;

&lt;p&gt;So why not give Conventional Commits a shot and experience the benefits for yourself? Happy coding!&lt;/p&gt;

</description>
      <category>git</category>
      <category>versioncontrol</category>
      <category>versioning</category>
      <category>software</category>
    </item>
    <item>
      <title>Cracking the code coverage myth</title>
      <dc:creator>Christian Ascone</dc:creator>
      <pubDate>Fri, 22 Sep 2023 16:47:20 +0000</pubDate>
      <link>https://dev.to/christianascone/cracking-the-code-coverage-myth-4530</link>
      <guid>https://dev.to/christianascone/cracking-the-code-coverage-myth-4530</guid>
      <description>&lt;p&gt;Writing software tests is one of those activities that can dramatically increase its robustness and reliability, allowing developers to correct and refactor code knowing that nothing has been damaged, however, test suites are made of code as well, written by humans and subject to bugs and incompleteness.&lt;/p&gt;

&lt;p&gt;Code coverage comes to our aid, but it can also be &lt;strong&gt;harmful&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is the code coverage?
&lt;/h1&gt;

&lt;p&gt;Let's start with the definition of Code coverage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code coverage is a metric that measures the extent to which the source code of a software program is tested by a test suite.&lt;/p&gt;

&lt;p&gt;It measures the percentage of code lines executed during the testing process.&lt;/p&gt;

&lt;p&gt;The purpose of code coverage analysis is to determine the effectiveness of the testing efforts by identifying areas of code that have been inadequately tested.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thanks ChatGpt for this definition.&lt;/p&gt;

&lt;p&gt;Giving a basic example, this is what happens for coverage calculation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff629x5dphdqz1t663j15.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff629x5dphdqz1t663j15.png" alt="Code coverage schema" width="800" height="808"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing more than this. Simple, right?&lt;/p&gt;

&lt;p&gt;This applies to conditional statements as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I was here&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// COVERAGE OK&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;It is 1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// COVERAGE OK&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;It is 2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// NO COVERAGE&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;It is 3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// NO COVERAGE&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// NO COVERAGE&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// COVERAGE OK&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Test&lt;/span&gt;
&lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three terms when we talk of code coverage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;hit&lt;/strong&gt;: the line of code has been executed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;partial:&lt;/strong&gt; the line of code has been executed, but there are remaining branches that were not executed (for example an if-else statement).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;miss:&lt;/strong&gt; the line of code has NOT been executed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These terms are needed to calculate the code coverage:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Coverage is the ratio of &lt;code&gt;hits / (sum of hit + partial + miss)&lt;/code&gt;. A code base that has 5 lines executed by tests out of 12 total lines will receive a coverage ratio of &lt;code&gt;41%&lt;/code&gt; (rounding down).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.codecov.com/docs/about-code-coverage#:~:text=Coverage%20is%20the%20ratio%20of,executed%20by%20a%20test%20suite."&gt;Codcov: About Code Coverage (Last seen in August 2023)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It quantifies how thoroughly our tests "exercise" our codebase and it's a valuable metric because it highlights untested or under-tested parts of your code, hence &lt;strong&gt;100% code coverage means that every single line of code is executed by at least 1 test function&lt;/strong&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  A warm, soft blanket during a blizzard
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; PASS  app/calculator.test.js
  ✓ add &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;---------------&lt;/span&gt;|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line &lt;span class="c"&gt;#s&lt;/span&gt;
&lt;span class="nt"&gt;---------------&lt;/span&gt;|---------|----------|---------|---------|-------------------
All files      |     100 |      100 |     100 |     100 |
 calculator.js |     100 |      100 |     100 |     100 | 
&lt;span class="nt"&gt;---------------&lt;/span&gt;|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.397 s, estimated 1 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wow, it appears that the code is fully tested!&lt;/p&gt;

&lt;p&gt;Code coverage can be comforting and can strengthen the confirmation bias that our code works and is robust when we are just chasing the goal of having the "green light", a seal of approval.&lt;/p&gt;

&lt;p&gt;Reality? Code coverage &lt;strong&gt;is nothing&lt;/strong&gt; when we talk about quality and it leads us to trust ourselves the code more than we should. And we're going to prove it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Some testing
&lt;/h2&gt;

&lt;p&gt;The roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Setup a javascript project with jest&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write a function to test (very simple)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write a test suite that reaches 100% coverage&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write a test suite that doesn't&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Initialize project&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm init

package name: &lt;span class="o"&gt;(&lt;/span&gt;dingdongbug&lt;span class="o"&gt;)&lt;/span&gt; codecoverage_example
version: &lt;span class="o"&gt;(&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;
description: Example project &lt;span class="k"&gt;for &lt;/span&gt;code coverage
entry point: &lt;span class="o"&gt;(&lt;/span&gt;index.js&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;test command&lt;/span&gt;: jest
git repository:
keywords:
author:
license: &lt;span class="o"&gt;(&lt;/span&gt;ISC&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"codecoverage_example"&lt;/span&gt;,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;,
  &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"Example project for code coverage"&lt;/span&gt;,
  &lt;span class="s2"&gt;"main"&lt;/span&gt;: &lt;span class="s2"&gt;"index.js"&lt;/span&gt;,
  &lt;span class="s2"&gt;"scripts"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"test"&lt;/span&gt;: &lt;span class="s2"&gt;"jest"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"author"&lt;/span&gt;: &lt;span class="s2"&gt;""&lt;/span&gt;,
  &lt;span class="s2"&gt;"license"&lt;/span&gt;: &lt;span class="s2"&gt;"ISC"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Install jest for testing&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; jest

added 291 packages, and audited 292 packages &lt;span class="k"&gt;in &lt;/span&gt;31s

31 packages are looking &lt;span class="k"&gt;for &lt;/span&gt;funding
  run &lt;span class="sb"&gt;`&lt;/span&gt;npm fund&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;details

found 0 vulnerabilities
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The folder structure should look as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tree &lt;span class="nt"&gt;-L&lt;/span&gt; 1
&lt;span class="nb"&gt;.&lt;/span&gt;
├── node_modules
├── package-lock.json
└── package.json

2 directories, 2 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Start coding
&lt;/h2&gt;

&lt;p&gt;We are going to update the &lt;code&gt;index.js&lt;/code&gt; adding our function to test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;longString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;_longString&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;longString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;isLong&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;longString&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;processInput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a very simple (and useless) function that just turns our string uppercase and finally adds a suffix if the text is longer than 5 characters.&lt;/p&gt;

&lt;p&gt;It returns an object with the resulting string and a boolean flag indicating if the string was long enough for a suffix.&lt;/p&gt;

&lt;p&gt;Let's keep going with test classes, starting from what will be called &lt;code&gt;sweetLie.test.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass a short string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;processInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass a long string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;processInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Longer string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test suite runs two tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The first one invokes the &lt;code&gt;processInput&lt;/code&gt; function with a &lt;strong&gt;short&lt;/strong&gt; string and checks the result is not null&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second one invokes the &lt;code&gt;processInput&lt;/code&gt; function with a string &lt;strong&gt;longer than 5&lt;/strong&gt; and checks the result is not null&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then we move on to the second suite: &lt;code&gt;bitterTruth.test.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass a short string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Short&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;processInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLong&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass an empty string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;processInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLong&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pass null&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;processInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is immediately evident how this suite goes deeper into testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The first test invokes the &lt;code&gt;processInput&lt;/code&gt; function with a &lt;strong&gt;short&lt;/strong&gt; string then checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The result is not null.&lt;/li&gt;
&lt;li&gt;The result object has the property &lt;code&gt;isLong&lt;/code&gt; equal to false.&lt;/li&gt;
&lt;li&gt;The transformed text is uppercase.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;The second test invokes the &lt;code&gt;processInput&lt;/code&gt; function with an &lt;strong&gt;empty&lt;/strong&gt; string then checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The result is not null.&lt;/li&gt;
&lt;li&gt;The result object has the property &lt;code&gt;isLong&lt;/code&gt; equal to false.&lt;/li&gt;
&lt;li&gt;The transformed text is equal to the initial input.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The last test invokes the &lt;code&gt;processInput&lt;/code&gt; function with a &lt;strong&gt;null&lt;/strong&gt; string checking that an error is thrown.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coverage
&lt;/h2&gt;

&lt;p&gt;Now let's try running our sweet lie suite with the coverage flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; sweetLie.test &lt;span class="nt"&gt;--coverage&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; codecoverage_example@1.0.0 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; jest &lt;span class="nt"&gt;--coverage&lt;/span&gt;

 PASS  ./sweetLie.test.js
  ✓ pass a short string &lt;span class="o"&gt;(&lt;/span&gt;4 ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓ pass a long string &lt;span class="o"&gt;(&lt;/span&gt;4 ms&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;----------&lt;/span&gt;|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line &lt;span class="c"&gt;#s&lt;/span&gt;
&lt;span class="nt"&gt;----------&lt;/span&gt;|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |
 index.js |     100 |      100 |     100 |     100 |
&lt;span class="nt"&gt;----------&lt;/span&gt;|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.168 s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yuppie 🎉 All tests passed and &lt;strong&gt;100% coverage&lt;/strong&gt;! No missing statements, branches, functions or lines, not at all.&lt;/p&gt;

&lt;p&gt;This is great, you will probably receive compliments and a pat on the back.&lt;/p&gt;

&lt;p&gt;You could say: "Yes, but during the code review someone will stop me and ask me to test further" and I hope so, but it doesn't always go like this, or you could inherit a project with these tests, trusting what you see in the coverage report.&lt;/p&gt;

&lt;p&gt;We can tell ourselves so many fairy tales, but this unfortunately remains a real case that can occur, if only because of the rush to release the product.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ddiiaqhkfukzd6jru15.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3ddiiaqhkfukzd6jru15.jpeg" alt="This is fine meme" width="800" height="378"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if we run the bitter truth? Let's see.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; npm &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; bitterTruth &lt;span class="nt"&gt;--coverage&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; codecoverage_example@1.0.0 &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; jest bitterTruth &lt;span class="nt"&gt;--coverage&lt;/span&gt;

 PASS  ./bitterTruth.test.js
  ✓ pass a short string &lt;span class="o"&gt;(&lt;/span&gt;5 ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓ pass an empty string &lt;span class="o"&gt;(&lt;/span&gt;1 ms&lt;span class="o"&gt;)&lt;/span&gt;
  ✓ pass null &lt;span class="o"&gt;(&lt;/span&gt;14 ms&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nt"&gt;----------&lt;/span&gt;|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line &lt;span class="c"&gt;#s&lt;/span&gt;
&lt;span class="nt"&gt;----------&lt;/span&gt;|---------|----------|---------|---------|-------------------
All files |   71.42 |       50 |     100 |   71.42 |
 index.js |   71.42 |       50 |     100 |   71.42 | 5-6
&lt;span class="nt"&gt;----------&lt;/span&gt;|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.262 s
Ran all &lt;span class="nb"&gt;test &lt;/span&gt;suites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apparently, the results are not so good: 50% of branches, 71% of statements which is lower than the 80% which is usually recommended.&lt;/p&gt;

&lt;p&gt;The truth is that we built a test suite which is more robust and valuable than the previous one, even if our coverage is lower, because we are testing some edge cases and checking the values inside the result object.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gPWLwKs---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1695328302370/1a5ffa31-ff9a-4b20-9e51-881ca3b01381.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gPWLwKs---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.hashnode.com/res/hashnode/image/upload/v1695328302370/1a5ffa31-ff9a-4b20-9e51-881ca3b01381.png" alt="Cases comparison" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Should I ignore code coverage?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzxpthkb7xi505yok17m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqzxpthkb7xi505yok17m.gif" alt="The Office NOOOO meme" width="220" height="179"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No, code coverage is a tool like many others and is not the problem itself, what is actually problematic is how much trust we place in it because we want to satisfy our thirst for safety.&lt;/p&gt;

&lt;p&gt;Code coverage offers simple but very important information: &lt;strong&gt;what's not been tested at all&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The focus of good test suites should always be on the expected behaviour, not on lines of code, however knowing that a block of code is never reached by tests helps us to be more aware that it might lead to unexpected results.&lt;/p&gt;

&lt;p&gt;As we have seen, while high code coverage demonstrates that a significant portion of the code has been tested, it does not guarantee the effectiveness or reliability of those tests.&lt;br&gt;&lt;br&gt;
Well-written tests, &lt;strong&gt;tailored to specific functionalities and edge cases&lt;/strong&gt;, are instrumental in ensuring that a project is robust and able to withstand real-world scenarios.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/christianascone/DingDongBug-Code-Coverage-Myth-Example"&gt;GitHub: Source code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codecoverage</category>
      <category>testing</category>
      <category>javascript</category>
      <category>bestpractices</category>
    </item>
  </channel>
</rss>
