<?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: Caleb Lewis</title>
    <description>The latest articles on DEV Community by Caleb Lewis (@calebdre).</description>
    <link>https://dev.to/calebdre</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%2F11417%2F3318929.jpeg</url>
      <title>DEV Community: Caleb Lewis</title>
      <link>https://dev.to/calebdre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/calebdre"/>
    <language>en</language>
    <item>
      <title>Simple Router for React Apps</title>
      <dc:creator>Caleb Lewis</dc:creator>
      <pubDate>Sat, 03 Sep 2022 22:33:23 +0000</pubDate>
      <link>https://dev.to/calebdre/simple-router-for-react-apps-40fo</link>
      <guid>https://dev.to/calebdre/simple-router-for-react-apps-40fo</guid>
      <description>&lt;p&gt;Tools like &lt;a href="https://github.com/remix-run/react-router"&gt;React Router&lt;/a&gt; provide a nice framework with which to think about and implement routing in your app. But what if you have a relatively small project that doesn't need complex routing logic?   &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enter:&lt;/strong&gt; the browser's &lt;code&gt;window.location&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;It provides all you need to create a simple 'routing' system for your app. Here's, for example, what the &lt;code&gt;window.location&lt;/code&gt; object would like for this url: &lt;code&gt;http://localhost:3000/books/123456?key=abcd&amp;amp;searchterm=cats&lt;/code&gt;:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yIPfq26E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkyc7ftvck27oc8frzya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yIPfq26E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkyc7ftvck27oc8frzya.png" alt="window.location object" width="880" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, what if we created a &lt;code&gt;Route&lt;/code&gt; functional component that parsed the &lt;code&gt;location.href&lt;/code&gt; and/or the &lt;code&gt;location.path&lt;/code&gt; looking for strict matches, and otherwise returning a 404 page.  &lt;/p&gt;

&lt;p&gt;Suppose we had a small library app and wanted to show just three pages: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a home page at &lt;code&gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a page to view many books at &lt;code&gt;/books&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;and a page to view an individual book's details: &lt;code&gt;/books/{bookID}&lt;/code&gt; where &lt;code&gt;bookID&lt;/code&gt; comes from a database (as-a-service like firebase).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Since only have a few simple requirements, we could just use &lt;code&gt;window.location&lt;/code&gt; to do our own routing.&lt;/p&gt;

&lt;p&gt;First we'll create an enum encapsulating the concept of having different 'pages' or routes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PBaCtAgZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6her0qb5863kj6s48b8m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PBaCtAgZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6her0qb5863kj6s48b8m.png" alt="routes enum code" width="880" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, functions to validate the home (&lt;code&gt;/&lt;/code&gt;) and books &lt;code&gt;(/books&lt;/code&gt;) routes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0BJXkZjh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cjbu2mu8hwmyqj7ne2hk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0BJXkZjh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cjbu2mu8hwmyqj7ne2hk.png" alt="valid books and home routes" width="880" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We return &lt;code&gt;null&lt;/code&gt; here to represent that the given &lt;code&gt;location&lt;/code&gt; is indeed not the one we want to render; we want to render the route that is alternatively returned.&lt;/p&gt;

&lt;p&gt;Now for the individual book route:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vhmnS7Z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/so7o5b0lsis89uaaz02t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vhmnS7Z_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/so7o5b0lsis89uaaz02t.png" alt="individual book route" width="880" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This illustrates how quick and easy it can be to make a simple and powerful routing mechanism. Here, we use regex to match against the location's pathname to strictly determine whether the path is a valid one. &lt;/p&gt;

&lt;p&gt;For variable paths and url parameters, it's simple to parse them from the url. Here's an example of getting a path parameter:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PcbdKMqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1utts8k0oq3v6zjr5g3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PcbdKMqi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q1utts8k0oq3v6zjr5g3.png" alt="path parameter fetching code" width="880" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and for url parameters:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hu4Cwpth--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vekshzlesr5rbmtfbeq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hu4Cwpth--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vekshzlesr5rbmtfbeq.png" alt="url parameter fetching code" width="880" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we can put these functions together inside the &lt;code&gt;Router&lt;/code&gt; component to render the correct content:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XnWlI6W_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4legkyvczkcd5oa7bh0d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XnWlI6W_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4legkyvczkcd5oa7bh0d.png" alt="router component code" width="880" height="1223"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we put the validators into an array, and iterate over them until we get a match. Once the first passing validation happens, &lt;code&gt;break&lt;/code&gt; takes us out of the iterator.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: that since we're iterating over the validation functions in an array, the order of the validations is something to pay attention to &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then we check to make sure that if &lt;code&gt;window.location&lt;/code&gt; didn't pass any of our validations, we set the route to &lt;code&gt;NOT_FOUND&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Finally, we check the route via a &lt;code&gt;switch&lt;/code&gt; statement, and show the page we want to render!  &lt;/p&gt;

&lt;p&gt;All you need next is to put &lt;code&gt;Router&lt;/code&gt; in your &lt;code&gt;index.js&lt;/code&gt; file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--58ROHM_V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvafqov5p2xm5zvbuzgb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--58ROHM_V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvafqov5p2xm5zvbuzgb.png" alt="index.js file" width="880" height="601"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Follow me on &lt;a href="https://twitter.com/caleb_dre"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Type Checking Styled Components</title>
      <dc:creator>Caleb Lewis</dc:creator>
      <pubDate>Mon, 08 Aug 2022 22:02:35 +0000</pubDate>
      <link>https://dev.to/calebdre/type-checking-styled-components-20nn</link>
      <guid>https://dev.to/calebdre/type-checking-styled-components-20nn</guid>
      <description>&lt;p&gt;I've just started using &lt;a href="https://styled-components.com/"&gt;styled components&lt;/a&gt; and it took &lt;strong&gt;way&lt;/strong&gt; too long to figure out how to get type checking for styled components with props:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hC5M4O3q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrmlejcqucq0xq5gmcf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hC5M4O3q--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qrmlejcqucq0xq5gmcf5.png" alt="styled component with error" width="880" height="389"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;If you're using typescript, your editor will give you this warning:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0DSn9i5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ro6lsy7zzc722diobex.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0DSn9i5U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ro6lsy7zzc722diobex.png" alt="the error" width="880" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Luckily, styled components can have generics types, so we just need to create a type for the its props and pass it as a generic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qOs26x26--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ogbepw99w8uk5w465mr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qOs26x26--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6ogbepw99w8uk5w465mr.png" alt="styled component without error" width="880" height="497"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No more type errors! :)&lt;/p&gt;

</description>
      <category>styledcomponents</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Input Mask Algorithm</title>
      <dc:creator>Caleb Lewis</dc:creator>
      <pubDate>Sun, 10 Jul 2022 20:32:22 +0000</pubDate>
      <link>https://dev.to/calebdre/input-mask-algorithm-33am</link>
      <guid>https://dev.to/calebdre/input-mask-algorithm-33am</guid>
      <description>&lt;h2&gt;
  
  
  What's an Input Mask?
&lt;/h2&gt;

&lt;p&gt;An &lt;a href="https://en.wikipedia.org/wiki/Input_mask" rel="noopener noreferrer"&gt;input mask&lt;/a&gt; is a template used to constrain a user's input. It can be used to create&lt;br&gt;
nicer form experiences by formatting their input as the user types:&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%2Fraw.githubusercontent.com%2Fcalebdre%2Fplayland%2Fmaster%2Fmask%2520demo.gif" 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%2Fraw.githubusercontent.com%2Fcalebdre%2Fplayland%2Fmaster%2Fmask%2520demo.gif" alt="demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Algorithm
&lt;/h2&gt;

&lt;p&gt;Here are the pieces of information we need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;input&lt;/code&gt;: the input from the user&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;template&lt;/code&gt;: the format we want the user's input to be in. (Almost, see &lt;code&gt;targetChar&lt;/code&gt;) any character can 
be used to represent phone numbers, dates, etc.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;targetChar&lt;/code&gt;: the character in &lt;code&gt;template&lt;/code&gt; that we'll replace with the user's input as they type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To solve, we'll use a straightforward approach: iterate through the &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;template&lt;/code&gt; strings, replacing &lt;br&gt;
&lt;code&gt;targetChar&lt;/code&gt; until one of them finishes first.  &lt;/p&gt;

&lt;p&gt;Let's create our masking function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mask&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="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetChar&lt;/span&gt;&lt;span class="p"&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;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;// final masked result&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;templateIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;// template pointer&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inputIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;// input pointer&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll need different index pointers for &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;template&lt;/code&gt; because their lengths and&lt;br&gt;
the &lt;code&gt;targetChar&lt;/code&gt; locations are arbitrary, so their indexes will need to be aligned manually&lt;br&gt;
as we iterate through the strings.&lt;/p&gt;

&lt;p&gt;We want to keep track of our offsets so that we can easily manipulate both offsets independently.&lt;br&gt;
On a given iteration, we don't want to increment the offset if we didn't replace a character from&lt;br&gt;
the mask (when &lt;code&gt;mask[maskIndex] !== maskChar&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;According to our approach, our stopping condition is when we've reached the end of either&lt;br&gt;
&lt;code&gt;input&lt;/code&gt; or &lt;code&gt;template&lt;/code&gt; strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;inputIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;input&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;amp;&amp;amp;&lt;/span&gt;
    &lt;span class="nx"&gt;templateIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;   
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At each iteration, we'll check whether the character at &lt;code&gt;templateIndex&lt;/code&gt; is&lt;br&gt;
&lt;code&gt;targetChar&lt;/code&gt; to decide whether we choose to add to the input from &lt;code&gt;template&lt;/code&gt;&lt;br&gt;
or from &lt;code&gt;input&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;templateIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;targetChar&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="nx"&gt;inputIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;templateIndex&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;Next, we need to align the indexes.   &lt;/p&gt;

&lt;p&gt;Normally, we'd always want to go to&lt;br&gt;
the next character at the end of the iteration, however we don't want to &lt;br&gt;
move on if a character isn't &lt;em&gt;used&lt;/em&gt;. A character from the template is always added to the output (&lt;code&gt;input&lt;/code&gt; is technically part of the &lt;code&gt;template&lt;/code&gt;) &lt;br&gt;
, but the input character only gets &lt;em&gt;used&lt;/em&gt; when a &lt;code&gt;targetChar&lt;/code&gt; is reached.&lt;/p&gt;

&lt;p&gt;Therefore, we'll only increment &lt;code&gt;inputIndex&lt;/code&gt; if we find &lt;code&gt;targetChar&lt;/code&gt;&lt;br&gt;
and increment &lt;code&gt;templateIndex&lt;/code&gt; at every iteration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;templateIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;targetChar&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="nx"&gt;inputIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="nx"&gt;inputIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="c1"&gt;// increment input&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&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="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;templateIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;templateIndex&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="c1"&gt;// increment template&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we return the output as a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;masked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using a mask, it's often beneficial to use &lt;strong&gt;two&lt;/strong&gt; inputs: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a visible one using the masked value to show to the user&lt;/li&gt;
&lt;li&gt;an invisible one that holds the raw value that the user never sees&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The raw value can be retrieved by removing any character that isn't &lt;code&gt;targetChar&lt;/code&gt;&lt;br&gt;
from the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// targetChar: '#', template: '###-###-####'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rawInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;maskedInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the whole function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mask&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="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetChar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="nx"&gt;input&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;replaceAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&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;templateIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;inputIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;input&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;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;templateIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&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;template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;templateIndex&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;targetChar&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&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="nx"&gt;inputIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="nx"&gt;inputIndex&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="k"&gt;else&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="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;templateIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nx"&gt;templateIndex&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;masked&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Closing thoughts
&lt;/h2&gt;

&lt;p&gt;It's fun to think about how extensible this can be; there can be a &lt;a href="https://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Parametric_polymorphism" rel="noopener noreferrer"&gt;polymorphic overload&lt;/a&gt; &lt;br&gt;
that accepts a function as a template to provide dynamic masks, or this would even be&lt;br&gt;
great as a sort of input middleware such that it's just one transformation in a set of&lt;br&gt;
other operations since it's &lt;a href="https://javascript.plainenglish.io/monads-for-javascript-developers-af29819823c" rel="noopener noreferrer"&gt;atomic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I ran into this problem while working on a bigger article on implementing phone login with Firebase and &lt;br&gt;
thought I'd share this fun little exercise. Stay tuned for the other article!&lt;br&gt;&lt;br&gt;
&lt;a href="https://twitter.com/caleb_dre" rel="noopener noreferrer"&gt;Follow me on Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;bonus: first person to prove they shipped this copy+pasted code gets $5 👀&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>computerscience</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
