<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>jameskerr.blog</title>
    <link>https://www.jameskerr.blog/</link>
    <description>Recent content on jameskerr.blog</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 22 Feb 2026 21:24:25 -0800</lastBuildDate><atom:link href="https://www.jameskerr.blog/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>I Will Not Become My Heros</title>
      <link>https://www.jameskerr.blog/posts/2026/i-will-not-become-my-heros/</link>
      <pubDate>Sun, 22 Feb 2026 21:24:25 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/2026/i-will-not-become-my-heros/</guid>
      <description>&lt;p&gt;A version of how I thought my life would look died today. Now my life will look different.&lt;/p&gt;
&lt;p&gt;I am afraid.&lt;/p&gt;
&lt;p&gt;There are thousands of other stories my life might tell, but I don’t know any of them. I haven’t imagined them once. This unknown future is terrifying for my ego and its need for control.&lt;/p&gt;
&lt;p&gt;I want to be important. I want to win life. I attach my worth to the outcome of riches, status, fame, intelligence, and independence. I know that life is for living, not winning. But why is it so hard to let it go?&lt;/p&gt;
&lt;p&gt;I do not know.&lt;/p&gt;
&lt;p&gt;I will not become my heros. I can’t.&lt;/p&gt;
&lt;p&gt;I will become my own self which has never been done before.&lt;/p&gt;
&lt;p&gt;I cannot control the outcomes of my efforts. I cannot win the games others have set up for me play. So many of us will not be famous. So many will not be rich. The games we play can only allow 1 winner and the rest of us.&lt;/p&gt;
&lt;p&gt;I can control what I do. I can control what I choose to do.&lt;/p&gt;
&lt;p&gt;I will be honest. I will be kind. I will make good things. I will forgive. I will have self-control. I will be for life. I will see the richness of life in the plants, rocks, animals and people around me. I will seek friendship. I will listen to children. I will be patient with immaturity. I look for the specialness in you, because if you are special, maybe so am I.&lt;/p&gt;
&lt;p&gt;God help me.&lt;/p&gt;
&lt;p&gt;I do not know my future, yet when I think of unknown possibilities, there is an excitement as well.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>I Lost My Airpods</title>
      <link>https://www.jameskerr.blog/posts/2026/i-lost-my-airpods/</link>
      <pubDate>Mon, 09 Feb 2026 10:01:50 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/2026/i-lost-my-airpods/</guid>
      <description>&lt;div class=&#34;poem&#34;&gt;Quiet the noise.
Uncomfortable, yet alluring.
You get used to it.
We get used to anything.
So choose carefully what to get used to.
Less.
Giving up. Missing out.
It&amp;rsquo;s not amazing.
It&amp;rsquo;s not revolutionary.
It&amp;rsquo;s quiet.
It&amp;rsquo;s simple.
It&amp;rsquo;s better.
There is more space to cry.
Rest is a shorter distance away.
Lingering on a word or a moment gets easier, naturally.
I miss the sounds.
I crave them.
But it&amp;rsquo;s hard to deny the ease of time passing by.
There&amp;rsquo;s a lot more inside than I was aware of.
Even when years pass and ability fades,
The treausre that hides within the mountain of my simple existence,
Is enough to keep things very interesting.
Until the end.
When the next adventure begins.&lt;/div&gt;

&lt;p&gt;The poem was written in a Pete&amp;rsquo;s Coffee in Lafayette, CA. It&amp;rsquo;s reveals my feelings after misplacing my AirPods, removing Spotify, and turning off all notifications on the Phone.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s better, but its not amazing. I like it, but it&amp;rsquo;s uncomfortable. By definition, stillness is not that exciting, but it is mysteriously enriching.&lt;/p&gt;
&lt;p&gt;I do hope I find my AirPods, but it&amp;rsquo;s pretty nice without them.&lt;/p&gt;
&lt;p&gt;Music feels like a treat. Maybe for the first time.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>It&#39;s hard to deny the simple beauty of a function</title>
      <link>https://www.jameskerr.blog/posts/2025/mostly-functions/</link>
      <pubDate>Tue, 02 Dec 2025 10:08:36 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/2025/mostly-functions/</guid>
      <description>&lt;p&gt;I love object-oriented programming. I love naming concepts and materializing them with a class. I love dot notation. I love hiding implementation behind a seemingly simple accessor. I&amp;rsquo;ve always leaned a little more on the side of OOP in the holy war against FP.&lt;/p&gt;
&lt;p&gt;But the more code I write, the harder it gets to deny the simple beauty of a function.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Data in, behavior, data out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Everything you need to understand it is at hand; no bouncing around the file to gather the necessary context. It&amp;rsquo;s all right there.&lt;/p&gt;
&lt;p&gt;The function is the &amp;ldquo;aha moment&amp;rdquo; of learning to program.&lt;/p&gt;
&lt;p&gt;As the years went by, I drifted away from using them because they look naive when put next to advanced constructs like classes, meta-programming, design patterns, or inheritance. But a few recent experiences might be changing that.&lt;/p&gt;
&lt;p&gt;In Sandy Metz&amp;rsquo;s book &lt;a href=&#34;https://sandimetz.com/99bottles&#34;&gt;&lt;strong&gt;99 Bottles of OOP&lt;/strong&gt;&lt;/a&gt;, she shocks readers with her initial implementation of a program to print the lyrics of the famous &amp;ldquo;99 Bottles of Beer&amp;rdquo; song.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;verse&lt;/span&gt;(number)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; number
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;when&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;No more bottles of beer on the wall, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no more bottles of beer.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Go to the store and buy some more, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;99 bottles of beer on the wall.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;when&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 bottle of beer on the wall, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 bottle of beer.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Take it down and pass it around, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;no more bottles of beer on the wall.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;when&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2 bottles of beer on the wall, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;2 bottles of beer.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Take one down and pass it around, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;1 bottle of beer on the wall.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;number&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; bottles of beer on the wall, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;number&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; bottles of beer.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Take one down and pass it around, &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;#{&lt;/span&gt;number&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt; bottles of beer on the wall.&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;\n&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Shameless simplicity. I can understand this code instantly. This was her default take and the most legible way to write it, even if it broke away from &amp;ldquo;best practices&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Just last month at the SF Ruby conference, well-published author Dave Thomas gave his talk about not needing the class construct for much of our programs. His point was, unless you need a bunch of different instances of a thing, use a function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Dave critiques this common ruby style.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;EmailService&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new(user)&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# And says why not just write it like this.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;EmailService&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;send(user)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I love when people in authority voice something we all intuitively know, but can&amp;rsquo;t put words to for fear of sounding naive.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It seems we need permission from respected people just to keep things simple.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And as a Ruby programmer, I&amp;rsquo;ve always typed &amp;ldquo;c&amp;rdquo; &amp;ldquo;l&amp;rdquo; &amp;ldquo;a&amp;rdquo; &amp;ldquo;s&amp;rdquo; &amp;ldquo;s&amp;rdquo; when I wanted to bring something into existence. But today I&amp;rsquo;ll pause and see if the simple function might be a better fit.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;module&lt;/span&gt; Summary
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;totals_by_category&lt;/span&gt;(scope)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scope
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;group(&lt;span style=&#34;color:#e6db74&#34;&gt;:category&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sum(&lt;span style=&#34;color:#e6db74&#34;&gt;:amount&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Someone on the internet remixed Michael Pollan&amp;rsquo;s famous advice about eating food, and it might be good advice to follow:&lt;/p&gt;
&lt;p&gt;Write code, not too much, mostly functions.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>DOM IDs are a real pain in my apps</title>
      <link>https://www.jameskerr.blog/posts/2025/refs-rails/</link>
      <pubDate>Wed, 17 Sep 2025 10:42:40 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/2025/refs-rails/</guid>
      <description>&lt;p&gt;The standard way to dynamically update HTML content in Ruby on Rails relies heavily on DOM IDs. These IDs are used to identify the target HTML element for an update. The ID must be present on the original page, and in the subsequent partial updates.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a very simple example of replacing content with a turbo stream.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;settings_form&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;turbo_stream&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;settings_form&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To me, this is a problem. My faulty human memory is tasked with keeping these IDs in sync. I will inevitably misspell an ID or forget to update an instance during a rename and won’t encounter the bug until I manually click test through the page update.&lt;/p&gt;
&lt;p&gt;During my work on &lt;a href=&#34;https://tend.cash&#34;&gt;Tend Cash&lt;/a&gt; over the last 2 years, I&amp;rsquo;ve tried to mitigate this. First I reached for the view_component gem, then tried regular view helpers, and finally created my own simple solution inspired by the way React manages &lt;a href=&#34;https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom&#34;&gt;references to HTML elements&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;solution-1-view_component&#34;&gt;Solution 1: view_component&lt;/h2&gt;
&lt;p&gt;Using the &lt;a href=&#34;https://github.com/ViewComponent/view_component&#34;&gt;view_component&lt;/a&gt; gem, I could encapsulate the ID within the component class. This allowed me to reference it within the template and in the controller during an update.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;SettingsFormComponent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;settings_form&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;%= id %&amp;gt;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;turbo_stream&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace &lt;span style=&#34;color:#66d9ef&#34;&gt;SettingsFormComponent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;id
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This was a little awkward because I needed the ID in the instance and on the class essentially. Too much overhead just to help with an ID.&lt;/p&gt;
&lt;h2 id=&#34;solution-2-view-helpers&#34;&gt;Solution 2: View Helpers&lt;/h2&gt;
&lt;p&gt;Next I went back to the defaults and just made &lt;a href=&#34;https://guides.rubyonrails.org/action_view_overview.html#overview-of-helpers-provided-by-action-view&#34;&gt;view helpers&lt;/a&gt; to keep the IDs safe.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;settings_form_id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;settings_form&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;%= settings_form_id %&amp;gt;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;turbo_stream&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace settings_form_id
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But I was longing for something better. At this point, I noticed that the method names and DOM ID strings were always the same. Some metaprogramming could help me avoid all that double typing. I wished for something that worked like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Refs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;define &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ref &lt;span style=&#34;color:#e6db74&#34;&gt;:settings_form&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;%= ref.settings_form %&amp;gt;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;turbo_stream&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace ref&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;settings_form
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;final-solution-refs&#34;&gt;Final Solution: Refs&lt;/h2&gt;
&lt;p&gt;And that&amp;rsquo;s what I&amp;rsquo;m using today. I made a very small library inspired by React&amp;rsquo;s concept of a &amp;ldquo;ref&amp;rdquo;. In React, a ref is a way to grab an HTML element without needing a string identifier.&lt;/p&gt;
&lt;p&gt;First I define all my string DOM IDs in a file called &lt;code&gt;config/refs.rb&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Refs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;define &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ref &lt;span style=&#34;color:#e6db74&#34;&gt;:settings_form&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then in my views and controllers I have access to this &amp;ldquo;ref&amp;rdquo; object with methods that match the identifiers I&amp;rsquo;ve defined.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;%= ref.settings_form %&amp;gt;&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;turbo_stream&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;replace ref&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;settings_form
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If a reference doesn&amp;rsquo;t exist, I get an error in Ruby when the template is being rendered. Much better.&lt;/p&gt;
&lt;h2 id=&#34;how-it-works&#34;&gt;How It Works&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s all of the code to copy &amp;amp; paste as you desire.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# lib/refs.rb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Refs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;(name)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    define_method(name) { name&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to_s }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;define&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;block)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    class_eval(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;block) &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; block_given?
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    @instance &lt;span style=&#34;color:#f92672&#34;&gt;||=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# app/controllers/application_controller.rb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Refs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;instance
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;helper_method &lt;span style=&#34;color:#e6db74&#34;&gt;:ref&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# config/initializers/refs.rb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Rails&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;application&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;to_prepare &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  load &lt;span style=&#34;color:#66d9ef&#34;&gt;Rails&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;lib&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;refs.rb&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  load &lt;span style=&#34;color:#66d9ef&#34;&gt;Rails&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;config&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;refs.rb&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# config/refs.rb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;Refs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;define &lt;span style=&#34;color:#66d9ef&#34;&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ref &lt;span style=&#34;color:#e6db74&#34;&gt;:settings_form&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;a-new-ruby-gem-refs-rails&#34;&gt;A New Ruby Gem: refs-rails&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve packaged up the code above into a ruby gem called &lt;a href=&#34;https://github.com/jameskerr/refs-rails&#34;&gt;refs-rails&lt;/a&gt;. Enjoy.&lt;/p&gt;
&lt;p&gt;Are you dealing with this ID problem in another way? I&amp;rsquo;d love to hear about it. Please reach out.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>When you put yourself out there, you’ll feel exposed</title>
      <link>https://www.jameskerr.blog/posts/2025/exposed/</link>
      <pubDate>Thu, 24 Jul 2025 20:19:32 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/2025/exposed/</guid>
      <description>&lt;p&gt;Yesterday, I got my first conference talk proposal rejection. It was going to be about folder structures, so I’m guessing there wasn’t enough AI to make the final cut.&lt;/p&gt;
&lt;p&gt;When I got the email, I felt both disappointed and relieved. It would have been a thrill to speak but I don’t have a ton of time and I love focusing on my main project &lt;a href=&#34;https://tend.cash&#34;&gt;Tend Cash&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In that arena, I was also rejected yesterday! Right after the kids went to bed, a user signed up, spent about 15 minutes scheduling her expenses, linked a bank, and then deleted her account. She responded to my intro email saying it wasn’t what she was looking for.&lt;/p&gt;
&lt;p&gt;As I was falling asleep, the feeling I had was like being out in the open sea without a sail. Like being in an open field with nowhere to hide from the enemy at the tree line. It was a vulnerable feeling. I was exposed.&lt;/p&gt;
&lt;p&gt;Then I remembered the common phrase: “put yourself out there.” It did feel like I was “out there” wherever “there” was. And that is exactly where I want to be! I want to speak at conferences and sell my own software and post my own articles.&lt;/p&gt;
&lt;p&gt;So today, I’m encouraged. If my goal is to put myself out there, it&amp;rsquo;s a good sign to feel exposed.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SuperDB Website Build Review</title>
      <link>https://www.jameskerr.blog/posts/superdb-website-build-review/</link>
      <pubDate>Thu, 13 Feb 2025 10:57:27 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/superdb-website-build-review/</guid>
      <description>&lt;p&gt;My work designing and developing the &lt;a href=&#34;https://superdb.org&#34;&gt;superdb.org&lt;/a&gt; website is finished. The first commit was on October 4th, 2024 and the final commit was on February 5th, 2025. It took 4 months of work with some holidays mixed in.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;./landing.png&#34;&gt;&lt;img src=&#34;landing.png&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;The landing page was the first thing I designed. I only planned to make a marketing site with a blog for the project, but later decided to include the docs as well.&lt;/p&gt;
&lt;p&gt;Last year, my company decided to change the name of our project from Zed to SuperDB. It was an exciting moment. Communicating what we are building became so much easier. That “DB” at the end signals where we fit in the market.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;./comic.png&#34;&gt;&lt;img src=&#34;comic.png&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;I played off the word &amp;ldquo;super&amp;rdquo; and ran with a superhero theme. I took inspiration from this Superman comic book, borrowing the colors, the 3D text, and “WHAM-O!” speech bubbles. It was a fun one to develop.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://jamieclarketype.com/project/rig-sans&#34;&gt;Rig Sans&lt;/a&gt; was the font I chose for body and headings. I picked a yellow accent color with red and blue secondary accents. No one ever asks for a “light mode”, so I themed it dark from the start.&lt;/p&gt;
&lt;p&gt;The old docs site contained over 100 pages of content. It got built using Docusaurus. There are quite a few things I don&amp;rsquo;t care for about Docusaurus (and all React-Based website builders). They make me relearn everything I know about building websites. I need to use their a brand new way to include a “script” tag for instance. The class names in Docusaurus are unreadable (they fixed this in recent versions), the way to extend it (swizzling) is complicated, and it’s slow to build because JavaScript does the building.&lt;/p&gt;
&lt;p&gt;This was the old site.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;./zed-docs.png&#34;&gt;&lt;img src=&#34;zed-docs.png&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;At this stage of my life, I’m drawn to simple, stable tools. I lean into vanilla HTML, JS, and CSS as much as I can. They are pretty freaking good now.&lt;/p&gt;
&lt;p&gt;I use &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt; for all my static site needs. It has downsides as well, but at least it’s fast, it lets me write standard HTML, CSS, and JS, and gives me good data structures for pages, sections, menus, markdown, metadata, and partials. The downside is having to write Go templates. It’s an awkward and verbose language. But the upsides are too good, so I’m getting over it.&lt;/p&gt;
&lt;p&gt;While working on this, I discovered even more features of Hugo like the &lt;a href=&#34;https://gohugo.io/getting-started/directory-structure/#union-file-system&#34;&gt;virtual union file system&lt;/a&gt;. That allowed me to render the docs in the site without moving the files into the website folder.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[[mounts]]
  source = &amp;#34;../super/docs&amp;#34;
  target = &amp;#34;content/docs&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I learned how to provide a &lt;a href=&#34;https://gohugo.io/render-hooks/code-blocks/&#34;&gt;custom renderer for code blocks&lt;/a&gt;. This let me render an interactive playground based on a block of code in the markdown.&lt;/p&gt;
&lt;p&gt;The code in this file…&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;a href=&#34;./file-system.png&#34;&gt;&lt;img src=&#34;file-system.png&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

￼
…will turn this code block in the markdown…&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;a href=&#34;./markdown.png&#34;&gt;&lt;img src=&#34;markdown.png&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

￼
…into this interactive playground on the website!
￼
&lt;figure&gt;&lt;a href=&#34;./playground.png&#34;&gt;&lt;img src=&#34;playground.png&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I used &lt;a href=&#34;https://turbo.hotwired.dev/&#34;&gt;@hotwired/turbo&lt;/a&gt; for all JavaScript interactions. It paired very nicely with Hugo.&lt;/p&gt;
&lt;p&gt;I learned that everything you setup in the &lt;code&gt;&amp;quot;turbo:load&amp;quot;&lt;/code&gt; event must get torn down in the &lt;code&gt;&amp;quot;turbo:before-cache”&lt;/code&gt; event. I felt pretty clever after discovering that &amp;ndash; until I stumbled across this &lt;a href=&#34;https://betterstimulus.com/integrating-libraries/lifecycle&#34;&gt;post at better-stimulus&lt;/a&gt;. The &amp;ldquo;good&amp;rdquo; way to integrate JS libraries is with a stimulus controller using the &lt;code&gt;connect()&lt;/code&gt; and &lt;code&gt;disconnect()&lt;/code&gt; callbacks. I also learned that each time you add &lt;code&gt;data-controller=“my-controller”&lt;/code&gt;, that element is attached to a single instance of the controller. Good to know.&lt;/p&gt;
&lt;p&gt;I got to play around with new more vibrant colors like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;@&lt;span style=&#34;color:#66d9ef&#34;&gt;supports&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;color&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;oklch&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;(&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;%&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;0&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;))&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  :&lt;span style=&#34;color:#a6e22e&#34;&gt;root&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    --color-accent: &lt;span style=&#34;color:#a6e22e&#34;&gt;oklch&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.93&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.2&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;102&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    --color-accent-2: &lt;span style=&#34;color:#a6e22e&#34;&gt;oklch&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;0.68&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0.27&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;30.01&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That overrides my previous color variables with the new fancy ones.&lt;/p&gt;
&lt;p&gt;The biggest question mark I had about the project was how to support multiple documentation “versions”. Hugo did not let me down. It took some deeper understanding of the data structures, but I had all the tools I needed. When I needed to build the versions dropdown popover, I reached for the css anchor positioning spec and &lt;a href=&#34;https://anchor-positioning.oddbird.net/&#34;&gt;grabbed a polyfill&lt;/a&gt; so that I could use the future syntax. I can’t wait until that is supported everywhere (as well as scroll animations).&lt;/p&gt;
&lt;p&gt;The final discovery to share is how I got the GitHub Star count in the header. Instead of using JavaScript to fetch the number after the page loads, I used Hugo to make the HTTP request at build time. The downside is that the number only gets updated when you deploy the site, but I like that no JS is involved. It&amp;rsquo;s pretty easy to trigger a deploy these days.&lt;/p&gt;
&lt;p&gt;Here was my template to grab the star count.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#a6e22e&#34;&gt;template&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;utils/star_count.html&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;brimdata/super&amp;#34;&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the implementation.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://api.github.com/repos/%s&amp;#34;&lt;/span&gt; . }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;try&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;resources&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;GetRemote&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;with&lt;/span&gt; .&lt;span style=&#34;color:#a6e22e&#34;&gt;Err&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;errorf&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%s&amp;#34;&lt;/span&gt; . }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; .&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt; | &lt;span style=&#34;color:#a6e22e&#34;&gt;transform&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stargazers_count&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;gt&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;thousands&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;math&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Div&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stargazers_count&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Printf&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;%.1f&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;thousands&lt;/span&gt; }}&lt;span style=&#34;color:#a6e22e&#34;&gt;k&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      {{ &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;count&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {{ &lt;span style=&#34;color:#a6e22e&#34;&gt;errorf&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Unable to get remote resource %q&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;$&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{{ &lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt; }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I loved working on this project. Learned a lot. Here&amp;rsquo;s the link again. Check it out.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://superdb.org&#34;&gt;superdb.org&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>useStateObject: A Simple, Convenient API Around useState</title>
      <link>https://www.jameskerr.blog/posts/use-state-object/</link>
      <pubDate>Thu, 15 Aug 2024 16:34:22 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/use-state-object/</guid>
      <description>&lt;p&gt;I am loving this API for working with React state. It&amp;rsquo;s a very light wrapper around useState. I call it &lt;code&gt;useStateObject&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how it looks:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useStateObject&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;offset&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s how I set it up with the initial state. I can access any of those state properties with dot syntax.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I get to use the following methods in my event handlers. This is how to merge another object with the state.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;offset&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here I provide a whole new value to the state.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;src&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;offset&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;200&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;size&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Just set one property.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setItem&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;offset&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;999&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reset the state back to its initial value.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;reset&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the code if you want to see what this API feels like in your own project.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;react&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useStateObject&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;initialState&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;setItem&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;prev&lt;/span&gt;) =&amp;gt; ({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;prev&lt;/span&gt;, [&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; }));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;newState&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;, ...&lt;span style=&#34;color:#a6e22e&#34;&gt;newState&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;reset&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;initialState&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip;and for you TypeScripters&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;react&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useStateObject&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;initialState&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;setItem&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;prev&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; ({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;prev&lt;/span&gt;, [&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; }));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;newState&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Partial&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;, ...&lt;span style=&#34;color:#a6e22e&#34;&gt;newState&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;reset() {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;initialState&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;StateObject&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;S&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;S&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ReturnType&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useStateObject&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Anyone want to figure out how to type the &lt;code&gt;setItem(key, value)&lt;/code&gt; method? Email me and I&amp;rsquo;ll update the post.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update August 22, 2024&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Thank you to the folks who responded to my request for TypeScript help. I have been taught that what the setItem arguments need is a implied generic type that extends a union of the state object&amp;rsquo;s keys (how&amp;rsquo;s that for some shop talk). Then I can use that generic to index the main state type, which is another implied generic. Nifty.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;setItem&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;keyof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;K&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The complete TypeScript version is thus:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;react&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useStateObject&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;initialState&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;init&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;setItem&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;keyof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;K&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;prev&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; ({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;prev&lt;/span&gt;, [&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;]&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; }));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;newState&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Partial&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;, ...&lt;span style=&#34;color:#a6e22e&#34;&gt;newState&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;reset&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setState&lt;/span&gt;({ ...&lt;span style=&#34;color:#a6e22e&#34;&gt;initialState&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &amp;ldquo;implied generics&amp;rdquo; work great when you pass a literal into the function, but in case you need to type the return value explicitly, here&amp;rsquo;s a utility type for that.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;StateObject&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;object&lt;/span&gt;&amp;gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;set&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;React&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Dispatch&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;React.SetStateAction&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;setItem&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;K&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;extends&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;keyof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;K&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;K&lt;/span&gt;]) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;merge&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;newState&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Partial&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;reset&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description>
    </item>
    
    <item>
      <title>The 3 Types of CSS Utility Classes</title>
      <link>https://www.jameskerr.blog/posts/3-types-of-css-utility-classes/</link>
      <pubDate>Mon, 01 Jul 2024 17:33:58 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/3-types-of-css-utility-classes/</guid>
      <description>&lt;p&gt;A 9.0 earthquake shook my front-end world when I discovered &amp;ldquo;layout primitives&amp;rdquo; at &lt;a href=&#34;https://every-layout.dev/&#34;&gt;every-layout.dev&lt;/a&gt;. Then an aftershock rolled through when I learned about fluid sizing and spacing tokens from &lt;a href=&#34;https://utopia.fyi/&#34;&gt;utopia.fyi&lt;/a&gt;. It&amp;rsquo;s completely changed the way I write HTML and CSS.&lt;/p&gt;
&lt;p&gt;Programming is the most fun when I am &lt;strong&gt;composing primitives.&lt;/strong&gt; If those primitives are at the right level of abstraction, programming feels like magic.&lt;/p&gt;
&lt;p&gt;In the aftermath of the quake, I am curiously focused on creating the perfect primitive CSS classes to compose in my HTML. In doing so, I have noticed three categories of classes emerging from the stylesheet.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Aesthetic classes&lt;/li&gt;
&lt;li&gt;Layout classes&lt;/li&gt;
&lt;li&gt;Spacing classes&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;note flow&#34;&gt;
  &lt;h6&gt;Note&lt;/h6&gt;
  Before this personal evolution, I thought it best practice to scope and isolate CSS, avoid utility classes, and name everything something semantic. It left me with code that was too coupled, hard to keep consistent, and difficult to understand a few months later. I queried Google for &amp;ldquo;Alternative to BEM CSS&amp;rdquo; which led me to Dave Rupert&amp;rsquo;s &lt;a href=&#34;https://daverupert.com/2022/08/modern-alternatives-to-bem/&#34;&gt;article&lt;/a&gt;, which led me to Andy Bell&amp;rsquo;s &lt;a href=&#34;https://cube.fyi/&#34;&gt;CUBE CSS&lt;/a&gt;. I&amp;rsquo;ve never looked back.
&lt;/div&gt;

&lt;h2 id=&#34;aesthetic-classes&#34;&gt;Aesthetic Classes&lt;/h2&gt;
&lt;p&gt;The sole concern of an aesthetic class is the look of the element. The &amp;ldquo;(B)locks&amp;rdquo; in CUBE. They are specific to the branding of the company or project. They will be named things like: &lt;code&gt;.card&lt;/code&gt;, &lt;code&gt;.aside&lt;/code&gt;, &lt;code&gt;.quote&lt;/code&gt;, &lt;code&gt;.note&lt;/code&gt;, &lt;code&gt;.toast&lt;/code&gt;, &lt;code&gt;.dialog&lt;/code&gt;, &lt;code&gt;.menu&lt;/code&gt;, and &lt;code&gt;.tab&lt;/code&gt; and contain properties like &lt;code&gt;color:&lt;/code&gt;, &lt;code&gt;background:&lt;/code&gt;, &lt;code&gt;border:&lt;/code&gt;, and &lt;code&gt;box-shadow:&lt;/code&gt;. They SHOULD NOT contain things like &lt;code&gt;padding:&lt;/code&gt;, &lt;code&gt;display:&lt;/code&gt;, or &lt;code&gt;margin:&lt;/code&gt;. Those go in the next two types.&lt;/p&gt;
&lt;h2 id=&#34;layout-classes&#34;&gt;Layout Classes&lt;/h2&gt;
&lt;p&gt;A layout class is solely focused on laying out elements. Not the look (above) and not the spacing between the elements (below). They are usually placed on the parent element and have names like &lt;code&gt;.center&lt;/code&gt;, &lt;code&gt;.cluster&lt;/code&gt;, &lt;code&gt;.repel&lt;/code&gt;, &lt;code&gt;.cover&lt;/code&gt;, &lt;code&gt;.stack&lt;/code&gt;, &lt;code&gt;.flow&lt;/code&gt;, &lt;code&gt;.prose&lt;/code&gt;, &lt;code&gt;.scroller&lt;/code&gt;, and &lt;code&gt;.switcher&lt;/code&gt;. The CSS properties within them should be things like: &lt;code&gt;display:&lt;/code&gt;, &lt;code&gt;align-items:&lt;/code&gt;, &lt;code&gt;justify-content:&lt;/code&gt;, &lt;code&gt;flex-wrap:&lt;/code&gt;, &lt;code&gt;grid-template-columns:&lt;/code&gt;, and &lt;code&gt;overflow:&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;spacing-classes&#34;&gt;Spacing Classes&lt;/h2&gt;
&lt;p&gt;Spacing classes are focused only on the space between elements that have been laid out with one of the classes above. If you have never surfed over to &lt;a href=&#34;https://utopia.fyi&#34;&gt;utopia.fyi&lt;/a&gt;, run as fast as you can to that link to generate fluid spacing and sizing tokens. Once you have those custom properties generated, use Tailwind or SCSS to generate spacing utility classes based on those spacing tokens (or just copy and paste some if you don&amp;rsquo;t have the tooling).&lt;/p&gt;
&lt;p&gt;Spacing classes should have names like &lt;code&gt;.gap-s&lt;/code&gt;, &lt;code&gt;.gap-m&lt;/code&gt;, &lt;code&gt;.gap-l&lt;/code&gt;, &lt;code&gt;.gutter-s&lt;/code&gt;, &lt;code&gt;.gutter-m&lt;/code&gt;, &lt;code&gt;.gutter-l&lt;/code&gt;, &lt;code&gt;.stack-s&lt;/code&gt;, &lt;code&gt;.stack-m&lt;/code&gt;, &lt;code&gt;stack-l&lt;/code&gt; and only contain properties like &lt;code&gt;gap:&lt;/code&gt;, &lt;code&gt;margin:&lt;/code&gt;, &lt;code&gt;padding:&lt;/code&gt;, and their variations.&lt;/p&gt;
&lt;h2 id=&#34;do-not-mix-contents&#34;&gt;DO NOT MIX CONTENTS&lt;/h2&gt;
&lt;p&gt;These CSS concerns are best not to be mixed into the same class. This is where I&amp;rsquo;ve gone wrong in the past. I used to write CSS like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;card&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h2&lt;/span&gt;&amp;gt;Summary&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h2&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt;&amp;gt;Here you&amp;#39;ll find the summary of the complicated data.&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/details&amp;#34;&lt;/span&gt;&amp;gt;View Details&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;card&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;white&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;border&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;solid&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;border);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  margin-inline: &lt;span style=&#34;color:#66d9ef&#34;&gt;auto&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;flex&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;flex-direction&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;column&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;gap&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;rem&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;box-shadow&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt; rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;h2&lt;/span&gt; &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;brand);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;text&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;less);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While the HTML looks clean, discovering what the CSS is doing is not immediately obvious. It is highly coupled to the markup and I will certainly be bouncing between to the two files to make changes.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s rewrite it using the three types of primitives described above. Split the aesthetic, layout, and spacing concerns into their own classes and compose a masterpiece in the HTML.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;card-1 stack box gap-xs&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h2&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;color-brand&amp;#34;&lt;/span&gt;&amp;gt;Summary&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h2&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;color-text-less&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Here you&amp;#39;ll find the summary of the complicated data.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;p&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/details&amp;#34;&lt;/span&gt;&amp;gt;View Details&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* Asthetic */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;card-1&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;white&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;box-shadow&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt; rgba(&lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;0.5&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;border&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;solid&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;border);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;color-brand&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;brand);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;color-text-less&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;text&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;less);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* Layout */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stack&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;flex&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;flex-direction&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;column&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;gap&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;space&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;s);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* Spacing */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;box&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;padding&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;space&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;s);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;gap-xs&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;/* Generated by tools (or not) */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;gap&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;space&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;xs);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;why-is-this-better&#34;&gt;Why is this better?&lt;/h2&gt;
&lt;p&gt;Each of those CSS classes is focused on one thing like all good software components are. Once they are written, I can reuse them over and over in my HTML. If I don&amp;rsquo;t like the look of &amp;ldquo;.card-1&amp;rdquo; for this element, I can swap it out for &amp;ldquo;.card-2&amp;rdquo; by changing one character in the HTML class. Once the core primitives are written, I don&amp;rsquo;t need to leave my HTML nearly as much to style things. It makes development very fun. This is why Tailwind took off.&lt;/p&gt;
&lt;h2 id=&#34;the-tailwind-phenomenon&#34;&gt;The Tailwind Phenomenon&lt;/h2&gt;
&lt;p&gt;For those of you thinking, &amp;ldquo;This is exactly what I already do with my Tailwind classes.&amp;rdquo; Well, it&amp;rsquo;s not quite the same. Tailwind does allow us to stay in the HTML, but in my opinion, its utilities are &lt;strong&gt;too primitive&lt;/strong&gt;. Heydon Pickering has a &lt;a href=&#34;https://heydonworks.com/article/what-is-utility-first-css/&#34;&gt;great article&lt;/a&gt; on this.&lt;/p&gt;
&lt;p&gt;For example, here I am centering a div with Tailwind.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;mx-auto max-w-5&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;Too Much Implementation Detail&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here it is with a &amp;ldquo;center&amp;rdquo; layout class.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;center&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;Much Easier To Read&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tailwind achieves that same thing as the &amp;ldquo;.center&amp;rdquo; layout class, but it shows all the implementation details. The &amp;ldquo;center&amp;rdquo; class is more descriptive, concise, and easier to remember.&lt;/p&gt;
&lt;p&gt;And for those of you thinking, &amp;ldquo;YUCK, utility classes are ruining front-end web development&amp;rdquo; like I was, remember &lt;strong&gt;composing primitives is what makes this job fun.&lt;/strong&gt; If you nail the primitives, programming feels like the sun on your back.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ship That Code • Publish That Post</title>
      <link>https://www.jameskerr.blog/posts/ship-that-code-publish-that-post/</link>
      <pubDate>Wed, 26 Jun 2024 11:38:31 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/ship-that-code-publish-that-post/</guid>
      <description>&lt;p&gt;Two articles I read this week have inspired the heck out of me. The first was by Jared Turner of thoughtbot with the punchy title &amp;ldquo;&lt;a href=&#34;https://thoughtbot.com/blog/wip-is-waste&#34;&gt;WIP is waste&lt;/a&gt;&amp;rdquo;. Jared described how &lt;strong&gt;&lt;em&gt;zero&lt;/em&gt;&lt;/strong&gt; value is delivered to the users of your software until it is shipped. The longer code changes remain on your machine, the more pure cost it is to the organization. Those words have been ringing in my head for days, and I have personal experience to confirm their truth.&lt;/p&gt;
&lt;p&gt;Nothing kills motivation than being stuck in a &amp;ldquo;code hole&amp;rdquo;. You know you&amp;rsquo;re in a code hole when you have 100+ type errors to fix, tests are not passing, you are &amp;ldquo;refactoring&amp;rdquo; something distantly related to what you set out to do, and the diff from main is so big the PR is unreviewable.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been there many times. It takes persistent effort to resist digging myself into one of these. I must remind myself to stop, focus on something small and ship it. It feels good! Get that little improvement out into the world. Take a small step toward the big vision.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ship that code.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The second article was about the &lt;a href=&#34;https://plausible.io/blog/open-source-saas&#34;&gt;journey of my favorite web analytics company&lt;/a&gt;, Plausible. They began in 2018. They raised zero dollars. They spent zero dollars on marketing. They simply wrote blogs. They were open and honest about everything. They had an ethic to their business practices, and it paid off. Their team of 4 made a million bucks in 2022 when the post was written. I love hearing stories like this. It&amp;rsquo;s exactly how I am going to grow and build &lt;a href=&#34;https://tend.cash&#34;&gt;Tend&lt;/a&gt;, my simple budgeting software.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;I&amp;rsquo;ll write my way out!&amp;rdquo; is what Hamilton sang in his musical and it&amp;rsquo;s true for software startups over 200 years later. Put another link on the internet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Publish that post.&lt;/strong&gt;&lt;/p&gt;
&lt;br/&gt;
&lt;br/&gt;
&lt;br/&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube.com/embed/_zhR6d6LDzM&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>How to Debug NodeJS with Breakpoints in VSCode</title>
      <link>https://www.jameskerr.blog/posts/debug-node-with-breakpoints-vscode/</link>
      <pubDate>Thu, 13 Jun 2024 15:29:18 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/debug-node-with-breakpoints-vscode/</guid>
      <description>&lt;p&gt;I made a quick screen recording using the VSCode debugger to break on a line of code in my NodeJS electron process. It&amp;rsquo;s a pretty nifty trick!&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube.com/embed/32Ddm7r4TPI&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

&lt;p&gt;*(main process, not main client 👍)&lt;/p&gt;
&lt;p&gt;Hope this helps someone!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The One Thing Missing in Every Budget App: Spending Commitments </title>
      <link>https://www.jameskerr.blog/posts/budget-apps-missing-spending-commitment/</link>
      <pubDate>Sun, 02 Jun 2024 08:34:44 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/budget-apps-missing-spending-commitment/</guid>
      <description>&lt;p&gt;The one thing missing in every budgeting application is the &amp;ldquo;Spending Commitment&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Definition: &amp;ldquo;A thing I am already committed to paying for&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This concept a fundamental part of any budgeting system. Why?&lt;/p&gt;
&lt;p&gt;Let’s back up. Why does one create a budget in the first place? I have thought hard to distill a clear answer for this. Here it is.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A good budget must answer:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Is my lifestyle sustainable?&lt;/li&gt;
&lt;li&gt;Should I buy this thing in front of me right now?&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;To answer the first question, I absolutely need to know how much money I’ve already committed to spending. These are all those repeating payments I have. This total will tell me if my lifestyle is sustainable. If it is not, I will need to make dramatic changes like move, get a cheaper car, or add a side job.&lt;/p&gt;
&lt;p&gt;To answer the second question, I need need to know how much cash is &lt;strong&gt;not&lt;/strong&gt; already committed. To get this number, I take my income and subtract the committed spending. This is my &amp;ldquo;free cash&amp;rdquo;. Can I buy this thing in front of me right now? If I have the free cash to spend, yes, otherwise no.&lt;/p&gt;
&lt;p&gt;In so many budgeting apps, this information is not prominently displayed.&lt;/p&gt;
&lt;p&gt;Take the classic bar chart of how much I&amp;rsquo;ve spent each month. This was my go-to chart in Mint for years. Since my committed and free spending are not separated, even if I was the most frugal I&amp;rsquo;ve ever been during one month, a single large yearly expense would make it appear that I overspent. The data is polluted. I would need to make an extra effort to see if I actually overspent or not. Maybe you&amp;rsquo;ve felt this pain too. We need to clearly separate the things we can control month-to-month (free spending) from the things we can&amp;rsquo;t (committed spending).&lt;/p&gt;
&lt;p&gt;A chart that only shows me how much &lt;em&gt;free cash&lt;/em&gt; I&amp;rsquo;ve spent each month would be so much more helpful. It would compare apples to apples. If the bar is high one month, it actually meant that I went crazy with the credit card.&lt;/p&gt;
&lt;p&gt;This separation of committed spending and free spending is so helpful for understanding your personal finances, I&amp;rsquo;m surprised it&amp;rsquo;s not built in to most budgeting apps.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s built in to &lt;a href=&#34;https://tend.cash&#34;&gt;Tend&lt;/a&gt;, though.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Fix for Kamal Deploy Assets Not Updating</title>
      <link>https://www.jameskerr.blog/posts/fix-for-kamal-deploy-assets-not-updating/</link>
      <pubDate>Tue, 23 Apr 2024 10:31:32 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/fix-for-kamal-deploy-assets-not-updating/</guid>
      <description>&lt;p&gt;Does this sound like you?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You are using Sprockets to pre-compile assets for production.&lt;/li&gt;
&lt;li&gt;You are using &lt;a href=&#34;https://kamal-deploy.org&#34;&gt;Kamal&lt;/a&gt; to deploy.&lt;/li&gt;
&lt;li&gt;You have &lt;a href=&#34;https://kamal-deploy.org/docs/configuration/asset-bridging/&#34;&gt;asset bridging&lt;/a&gt; enabled in your deploy.yml file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If that is you, you will find that your assets are not updated about 50% of the time. Randomly.&lt;/p&gt;
&lt;p&gt;The fix is to disable asset bridging in deploy.yml.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# comment this out 👇&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;asset_path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/rails/public/assets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;note flow&#34;&gt;
  &lt;h6&gt;April 25, 2024 Update&lt;/h6&gt;
  Wait! There is another way to fix this without disabling asset bridging. See the section at the end of this post. But keep reading to understand the problem.
&lt;/div&gt;

&lt;p&gt;Why?&lt;/p&gt;
&lt;p&gt;When the deploy pipeline runs &lt;code&gt;rails assets:precompile&lt;/code&gt; a file will be created called &lt;code&gt;.sprockets-manifest-randomhex.json&lt;/code&gt;. This manifest file maps the name of your asset to the digested name that will be used in production for &lt;code&gt;stylesheet_link_tag&lt;/code&gt; and the like.&lt;/p&gt;
&lt;p&gt;From the &lt;a href=&#34;https://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets&#34;&gt;Rails Guide:&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The command also generates a .sprockets-manifest-randomhex.json (where randomhex is a 16-byte random hex string) that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods to avoid handing the mapping requests back to Sprockets.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now what is asset bridging in kamal? Here a &lt;a href=&#34;https://kamal-deploy.org/docs/configuration/asset-bridging/&#34;&gt;snippet from their docs&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If there are changes to CSS or JS files, we may get requests for the old versions on the new container and vice-versa. To avoid 404s we can specify an asset path. Kamal will replace that path in the container with a mapped volume containing both sets of files. This requires that file names change when the contents change (e.g. by including a hash of the contents in the name).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You can enable this behavior by specifying the &lt;code&gt;asset_path:&lt;/code&gt; option in your deply.yml.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# Again, this is the problem.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;asset_path&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;/rails/public/assets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this option set, kamal will keep both the current and the previous versions of the assets on your server. That means &lt;strong&gt;there will be two&lt;/strong&gt; &lt;code&gt;.sprockets-manifest-randomhex.json&lt;/code&gt; files. The Rails server &lt;strong&gt;will choose the first one it finds!&lt;/strong&gt; It might be the new one, it might be the old one depending on where that random hex puts it on the file system.&lt;/p&gt;
&lt;p&gt;The fix for now is to &lt;strong&gt;turn off asset bridging&lt;/strong&gt;. The cost is low. It will only affect people who have in-flight requests right as the server restarts. They would just have to re-load the page again.&lt;/p&gt;
&lt;p&gt;In the future, this could be fixed by only keeping one version of the manifest file, or specifiying which manifest to use somehow.&lt;/p&gt;
&lt;p&gt;Thank you so much to reddit user &lt;a href=&#34;https://www.reddit.com/user/ignurant/&#34;&gt;ignurant&lt;/a&gt; for their detailed explanation in &lt;a href=&#34;https://www.reddit.com/r/ruby/comments/17jmert/comment/k72v7m2/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button&#34;&gt;this comment&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;update-on-april-25-2024&#34;&gt;Update on April 25, 2024&lt;/h2&gt;
&lt;p&gt;There is another solution that allows you to still bridge the assets. You explicitly set the name of the manifest file in your &lt;code&gt;production.rb&lt;/code&gt; file like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-rb&#34; data-lang=&#34;rb&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# config/environments/production.rb&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;config&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;assets&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;manifest &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Rails&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;root&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;join(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;config&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;manifest.json&amp;#39;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This removes the random hash, allowing the new file to overwrite the old one on deploy. I don&amp;rsquo;t know the reason for the random hash by default or the implications of removing it, but this would fix the problem outlined in this post. Thank you to you reddit user &lt;a href=&#34;https://www.reddit.com/user/skp_/&#34;&gt;_skp&lt;/a&gt; for their help in this &lt;a href=&#34;https://www.reddit.com/r/ruby/comments/1cbavse/comment/l14atcy/?utm_source=share&amp;amp;utm_medium=web3x&amp;amp;utm_name=web3xcss&amp;amp;utm_term=1&amp;amp;utm_content=share_button&#34;&gt;comment&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to Set Output Values in Github Actions</title>
      <link>https://www.jameskerr.blog/posts/how-to-set-output-in-github-actions/</link>
      <pubDate>Mon, 22 Apr 2024 14:15:10 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/how-to-set-output-in-github-actions/</guid>
      <description>&lt;p&gt;This is the syntax for setting an output parameter in a Github Actions step.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;echo &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;{name}={value}&amp;#34;&lt;/span&gt; &amp;gt;&amp;gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;$GITHUB_OUTPUT&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s an example of using this syntax in a workflow step.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Expose the artifact path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;paths&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;echo &amp;#34;artifact=&amp;#39;app-setup.exe&amp;#39;&amp;#34; &amp;gt;&amp;gt; &amp;#34;$GITHUB_OUTPUT&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s an example of using the output of a command as the value.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;echo &amp;#34;artifact=&amp;#39;$(yarn run artifact-path)&amp;#39;&amp;#34; &amp;gt;&amp;gt; &amp;#34;$GITHUB_OUTPUT&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And here is how you would access that output value in a later step.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;- &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Later Step&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;./bin/codesign ${{ steps.paths.outputs.artifact }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The syntax is &lt;code&gt;steps.[step_id].outputs.[output_name]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It took me several attempts to discover this snippet on the Github Actions docs, so I decided to &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-output-parameter&#34;&gt;post it here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Happy hacking.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Responsive Sizing for React Arborist Tree Component</title>
      <link>https://www.jameskerr.blog/posts/responsive-sizing-for-react-arborist-tree-component/</link>
      <pubDate>Wed, 17 Apr 2024 10:00:47 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/responsive-sizing-for-react-arborist-tree-component/</guid>
      <description>&lt;p&gt;Many people ask me about how to dynamically set the width and height of the Tree component based on its parent using &lt;a href=&#34;https://github.com/brimdata/react-arborist&#34;&gt;react-arborist&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;TreeView&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt;} &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// How do I make these responsive?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The component only accepts fixed pixels for the height and width because it virtualizes the rows. It uses the tree height, scroll position, and row height to render only the nodes that are visible to the user. Nodes are mounted as soon as they are scrolled into view and unmounted when they overflow.&lt;/p&gt;
&lt;p&gt;This is great, but I almost always want my tree to be the same width and height of its parent, not a fixed size.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how to do it.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s get the HTML marked up so that the parent will grow and shrink as the window resizes.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;className&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sidebar&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;className&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tree-parent&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;TreeView&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We will first style the &lt;code&gt;.sidebar&lt;/code&gt; class.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sidebar&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;vh&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;display&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;flex&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;flex-direction&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;column&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We make this element as high as the viewport and 300px wide to make it look like a sidebar. Then we make it a flex parent setting the flex-direction to &amp;ldquo;column&amp;rdquo; so the children stack vertically.&lt;/p&gt;
&lt;p&gt;Next is the &lt;code&gt;.tree-parent&lt;/code&gt; class.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;tree-parent&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;flex-grow&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  min-block-size: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here we make the element grow to fill all available empty space and set the min-block-size to zero.&lt;/p&gt;
&lt;div class=&#34;note flow&#34;&gt;
  &lt;h6&gt;Note&lt;/h6&gt;
  I always forget about the min-block-size (min-height). If you omit this style, growing the parent will work fine, but shrinking it will not shrink the tree. The reason is the default value for min-height is &amp;ldquo;auto&amp;rdquo;. The browser will consider how tall the children are to determine the height of the parent. The issue is we want the the child (tree) to set its height to whatever the parent is. It&amp;rsquo;s like a co-dependent DOM relationship. Setting the minimum height to zero tells the element not to concern itself with its children&amp;rsquo;s height. Just grow and shrink as if you have no children.
&lt;/div&gt;

&lt;p&gt;To summarize, we tell this element to grow to fill all available space, and always shrink if your parent gets smaller. Don&amp;rsquo;t worry about what your children are doing.&lt;/p&gt;
&lt;p&gt;Now we need to get the size of the &lt;code&gt;.tree-parent&lt;/code&gt; element and pass those pixels to the &lt;code&gt;&amp;lt;TreeView /&amp;gt;&lt;/code&gt; component, while responding to resizing. For this we reach for the &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver&#34;&gt;ResizeObserver&lt;/a&gt; DOM API.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;TreeView&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;react-arborist&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useResizeObserver&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;use-resize-observer&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Sidebar&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt; } &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useResizeObserver&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;className&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;sidebar&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;className&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tree-parent&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;}&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;TreeView&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;width&lt;/span&gt;} &lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;height&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We could write the hook from scratch, but it will probably end up looking exactly like this small, well-written library &lt;a href=&#34;https://github.com/ZeeCoder/use-resize-observer&#34;&gt;use-resize-observer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You attach the ref to the &lt;code&gt;.tree-parent&lt;/code&gt; and now you have its width and height in pixels. Those variables will be updated every time that element is resized in the DOM and the component will re-render with updated width and height values.&lt;/p&gt;
&lt;p&gt;This is exactly where we want to be. We can pass those dimensions to the &lt;code&gt;&amp;lt;TreeView /&amp;gt;&lt;/code&gt; component and it will always be the same size as its parent.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Cache the Result of a JavaScript Getter Method</title>
      <link>https://www.jameskerr.blog/posts/cache-the-result-of-a-javascript-getter-method/</link>
      <pubDate>Tue, 16 Apr 2024 12:49:54 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/cache-the-result-of-a-javascript-getter-method/</guid>
      <description>&lt;p&gt;My first programming language was Ruby. Well, it was actually Microsoft Excel, then VBA, then C# for a university class, then Ruby.&lt;/p&gt;
&lt;p&gt;Ruby&amp;rsquo;s syntax is designed for developer happiness. What a wonderful goal. In Ruby, you don&amp;rsquo;t need to add parenthesis to method calls if there are no arguments.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FSEntry&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;File&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stat(path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# And you can call it like this&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;entry &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Entry&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;new
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;entry&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stat &lt;span style=&#34;color:#75715e&#34;&gt;# no parens!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However, when I don&amp;rsquo;t see parens it&amp;rsquo;s not obvious whether this is a cheap or expensive operation. In this case, we are hitting the file system each time we call it.&lt;/p&gt;
&lt;p&gt;So it&amp;rsquo;s a common practice in Ruby to &amp;ldquo;memoize&amp;rdquo; expensive methods. In simpler terms, to cache the result of a method body in a private instance variable.&lt;/p&gt;
&lt;p&gt;This is how it looks in Ruby.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ruby&#34; data-lang=&#34;ruby&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stat&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  @_stat &lt;span style=&#34;color:#f92672&#34;&gt;||=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;File&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;stat(path)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I love what I call the &amp;ldquo;or-equals&amp;rdquo; operator in Ruby. Others call it the &amp;ldquo;short-circuit&amp;rdquo; operator. It works like this. If the variable &lt;code&gt;@_stat&lt;/code&gt; is falsey, it will run &lt;code&gt;File.stat&lt;/code&gt; and store the result in the &lt;code&gt;@_stat&lt;/code&gt; variable. Finally, since Ruby always returns the result of the last expression in a block or method, the resulting &lt;code&gt;@_stat&lt;/code&gt; value is returned. This has the effect of only running &lt;code&gt;File.stat&lt;/code&gt; the first time this method is invoked. All other times, we just get the cached &lt;code&gt;@_stat&lt;/code&gt; value.&lt;/p&gt;
&lt;p&gt;Can this type of thing be done in JavaScript?&lt;/p&gt;
&lt;p&gt;Not so long ago, JavaScript added &amp;ldquo;getter&amp;rdquo; and &amp;ldquo;setter&amp;rdquo; methods to classes. Like Ruby, you can call these methods without parenthesis.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FSEntry&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stats&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;statsSync&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;entry&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FSEntry&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;entry&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stats&lt;/span&gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// No parens!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now to cache the result, we can port the same strategy we used in Ruby. Create a private instance variable to cache the result the first time the method is called.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FSEntry&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stats&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_stats&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_stats&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;statsSync&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_stats&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This gets the job done, but it&amp;rsquo;s painfully verbose. No developer happiness increase. And if you&amp;rsquo;re using TypeScript it&amp;rsquo;s even worse.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FSEntry&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;_stats&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Stats&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stats() {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_stats&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_stats&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;statsSync&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;_stats&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is ridiculous.&lt;/p&gt;
&lt;p&gt;After some thought, I found a way to reign in the chaos. First, I made a utility function off in a different file that looks like this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This function checks for a property &lt;code&gt;prop&lt;/code&gt; on the &lt;code&gt;self&lt;/code&gt; argument. If it exists, return it. If not, run the &lt;code&gt;func&lt;/code&gt; and assign the result to that property and return it.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how we call it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;../utils.js&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FSEntry&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;get&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stats&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;_stats&amp;#34;&lt;/span&gt;, () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;statSync&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we&amp;rsquo;re back! To a one-liner! Now we can cache methods all over the place.&lt;/p&gt;
&lt;p&gt;What about Type Safety?&lt;/p&gt;
&lt;p&gt;All you TypeScript purists may wince at that memo function. But to me, the tradeoff is so so worth it. Here&amp;rsquo;s how I typed the cache function in my app.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;object&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;)&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!==&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;self&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;prop&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The return type of the &lt;code&gt;func&lt;/code&gt; parameter is inferred and used as the return type for the whole &lt;code&gt;cache&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Pretty sweet.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Friend Is You</title>
      <link>https://www.jameskerr.blog/posts/the-friend-is-you/</link>
      <pubDate>Tue, 16 Apr 2024 07:46:19 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/the-friend-is-you/</guid>
      <description>&lt;p&gt;Imagine hanging out with someone all day who berates you. They make final judgements about your character. They criticize anything you do imperfectly. They gossip about everyone you meet. They treat everyone like this. They even shame you for having such a shitty friend around all the time. You desperately want them to go away, but they won&amp;rsquo;t stop following you. They never leave. The moment you wake up until the moment you fall asleep. Every conscious moment, their stream of negative comments flows into your ear.&lt;/p&gt;
&lt;p&gt;What would you do?&lt;/p&gt;
&lt;p&gt;You would escape.&lt;/p&gt;
&lt;p&gt;You would fill your life with noise to distract yourself them. When you are inevitably alone, you would do anything to alter your mind and shut them out for a few moments because solitude is abusive.&lt;/p&gt;
&lt;p&gt;Now imagine a true best friend. They arrive the moment you arise. This friend knows everything you&amp;rsquo;ve ever done, good and bad. They know. They care. They love you. All day, they listen to you. They never make a final judgement. They say things that encourage you to live into higher and higher versions of yourself. They fully accept where you&amp;rsquo;re at today and they understand the difficult moments you&amp;rsquo;ll feel as you grow. They are fascinated by your unique personality. They don&amp;rsquo;t gossip about other people. In fact, they see everyone like this. Even your enemies. They only speak truth to you in the service of love.&lt;/p&gt;
&lt;p&gt;What would you do?&lt;/p&gt;
&lt;p&gt;The sky&amp;rsquo;s the limit. Solitude is no longer scary, it is grounding.&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t get to choose many things, but we get to choose our friends.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Finally Understanding How Array.sort(comparator) Works</title>
      <link>https://www.jameskerr.blog/posts/javascript-sort-comparators/</link>
      <pubDate>Thu, 28 Mar 2024 11:39:07 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/javascript-sort-comparators/</guid>
      <description>&lt;p&gt;After 13 years of JavaScript, I finally have a way to remember how the comparator function in &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort&#34;&gt;Array.sort()&lt;/a&gt; works.&lt;/p&gt;
&lt;p&gt;I think the trouble is that all the examples use this shorthand syntax.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;array&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;b&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// too hard for James
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is beyond confusing for me. In the past, I would just try &lt;code&gt;b - a&lt;/code&gt; then try &lt;code&gt;a - b&lt;/code&gt; and pick which one gave me the result I wanted. But now I have a mental model simple enough for me to remember.&lt;/p&gt;
&lt;p&gt;First, the sole purpose of the comparator function is to answer this quesions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Where should &amp;ldquo;a&amp;rdquo; be placed in the new sorted array? To the left of &amp;ldquo;b&amp;rdquo; or to the right?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The arguments passed to the comparator function are usually named &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt;. This makes sense to me, since the first arg comes before the second arg, and &lt;code&gt;a&lt;/code&gt; comes before &lt;code&gt;b&lt;/code&gt; in the English alphabet. These arguments represent two items in the array.&lt;/p&gt;
&lt;p&gt;Now let&amp;rsquo;s think about the return value. The function must return a number. Numbers exist on a number line going from left to right just like the items in an array. The negative numbers are on the left, zero is in the middle, and the positive numbers on the right.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;-3   -2   -1   0   1   2   3
----------------------------
    a good ol&amp;#39; number line
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So check it out, if your comparator function returns a &lt;em&gt;negative number&lt;/em&gt;, the first argument &lt;code&gt;a&lt;/code&gt; will come first, before &lt;code&gt;b&lt;/code&gt;. Just like negative numbers on the number line come first!&lt;/p&gt;
&lt;p&gt;If the function returns a &lt;em&gt;positive number&lt;/em&gt;, the first argument &lt;code&gt;a&lt;/code&gt; will come after &lt;code&gt;b&lt;/code&gt;. The &lt;code&gt;a&lt;/code&gt; item will be on the &amp;ldquo;right&amp;rdquo; side of of &lt;code&gt;b&lt;/code&gt;, just like the positive numbers are on the &amp;ldquo;right&amp;rdquo; side of the number line!&lt;/p&gt;
&lt;h1 id=&#34;heading&#34;&gt;🤯&lt;/h1&gt;
&lt;p&gt;If the function returns 0, there will be no change to the existing order of the elements. This one was pretty easy to remember.&lt;/p&gt;
&lt;p&gt;To summarize, we just want to find out where &lt;code&gt;a&lt;/code&gt; goes. Does it go to the left or right of &lt;code&gt;b&lt;/code&gt;. Negative means left, positive means right. Number line. Left to right.&lt;/p&gt;
&lt;p&gt;The &amp;ldquo;left-to-right-ness&amp;rdquo; of the number line, the items in an array, the alphabet, and the positional arguments all finally clicked for me today.&lt;/p&gt;
&lt;p&gt;Maybe this will click for you too and you can save yourself 13 years of googling &amp;ldquo;How does array.sort(comparator) work again?&amp;rdquo;.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Using @monaco-editor/react in Electron without Internet Connection</title>
      <link>https://www.jameskerr.blog/posts/offline-monaco-editor-in-electron/</link>
      <pubDate>Wed, 27 Mar 2024 16:58:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/offline-monaco-editor-in-electron/</guid>
      <description>&lt;p&gt;The &lt;a href=&#34;https://microsoft.github.io/monaco-editor/&#34;&gt;Monaco Editor&lt;/a&gt; is awesome. It&amp;rsquo;s what powers VSCode. I wanted to use it to power the query editor pane in &lt;a href=&#34;https://zui.brimdata.io/&#34;&gt;Zui&lt;/a&gt;, the data exploration app I work on. I reached for &lt;a href=&#34;https://github.com/suren-atoyan/monaco-react&#34;&gt;@monaco-editor/react&lt;/a&gt; because it fit well in the app&amp;rsquo;s NextJS, React, and Electron tech stack. Loading the actual &lt;a href=&#34;https://www.npmjs.com/package/monaco-editor&#34;&gt;monaco-editor&lt;/a&gt; code seems to be a bit of a headache, so in an effort to make setup simple, @monaco-editor/react fetches the minified monaco-editor files from &lt;a href=&#34;https://www.jsdelivr.com/&#34;&gt;jsdelivr&lt;/a&gt; over the network.&lt;/p&gt;
&lt;p&gt;This was a problem. Zui should work without needing to be connected to the internet.&lt;/p&gt;
&lt;p&gt;My solution involved a configuration change in @monaco-editor/react, custom protocol handlers in electron, and disabling the webSecurity option in my browser window instances (only in development).&lt;/p&gt;
&lt;p&gt;The first step was to add the &lt;a href=&#34;https://www.npmjs.com/package/monaco-editor&#34;&gt;monaco-editor&lt;/a&gt; package to my dependencies.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;yarn add monaco-editor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I configured the &lt;code&gt;@monaco-editor/react&lt;/code&gt; loader to fetch the files from my own made up url. This code should run in the browser window before react mounts up.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;loader&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;@monaco-editor/react&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// This is how you change the source of the monaco files.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;loader&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;config&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;paths&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;vs&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;app-asset://zui/node_modules/monaco-editor/min/vs&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What&amp;rsquo;s the deal with that url? I just made it up. It can be anything because I will intercept requests to it in electron&amp;rsquo;s main process using the &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/protocol&#34;&gt;protocol&lt;/a&gt; module. Then I can fetch the appropriate file from the node_modules directory on disc and return it.&lt;/p&gt;
&lt;p&gt;Here was my finished code. This should run in the main process during initialization before any browser windows are created.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;protocol&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;electron&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;AssetUrl&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;../protocols/asset-url&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;AssetServer&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;../protocols/asset-server&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;protocol&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;registerSchemesAsPrivileged&lt;/span&gt;([
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;scheme&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;app-asset&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;privileges&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;standard&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;supportFetchAPI&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;bypassCSP&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AssetServer&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;whenReady&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;then&lt;/span&gt;(() &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;protocol&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;handle&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;app-asset&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;asset&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AssetUrl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;request&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;asset&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNodeModule&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromNodeModules&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;asset&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;relativeUrl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;server&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fromPublic&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;asset&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;relativeUrl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First we register the &lt;code&gt;&amp;quot;app-asset&amp;quot;&lt;/code&gt; scheme with &amp;ldquo;standard&amp;rdquo; privileges, making it behavie like the &lt;code&gt;&amp;quot;http&amp;quot;&lt;/code&gt; scheme. Read more about that &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/protocol#protocolregisterschemesasprivilegedcustomschemes&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Then I new up my little &lt;code&gt;AssetServer&lt;/code&gt; class which is responsible for finding the files on the file system to return to the requestor. That code is posted below.&lt;/p&gt;
&lt;p&gt;Once the app fires the &lt;code&gt;&amp;quot;ready&amp;quot;&lt;/code&gt; event, I intercept all requests to the &lt;code&gt;&amp;quot;app-asset&amp;quot;&lt;/code&gt; scheme, using &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/protocol#protocolhandlescheme-handler&#34;&gt;protocol.handle()&lt;/a&gt;. If the pathname starts with &amp;ldquo;/node_modules&amp;rdquo; I look it up using Node&amp;rsquo;s &lt;a href=&#34;https://nodejs.org/api/modules.html#requireresolverequest-options&#34;&gt;require.resolve&lt;/a&gt; and return it. Otherwise I look for that file in the public directory and return it.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the code for the &lt;code&gt;AssetServer&lt;/code&gt; class.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;net&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;electron&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node:path&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;pathToFileURL&lt;/span&gt; } &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;node:url&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AssetServer&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;fromNodeModules&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;relativePath&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;require&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;resolve&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;relativePath&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pathToFileURL&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;net&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;, { &lt;span style=&#34;color:#a6e22e&#34;&gt;bypassCustomProtocolHandlers&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;fromPublic&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;relativeUrl&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;join&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;app&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;getAppPath&lt;/span&gt;(), &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;out&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;relativeUrl&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;pathToFileURL&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;net&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;, { &lt;span style=&#34;color:#a6e22e&#34;&gt;bypassCustomProtocolHandlers&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the &lt;code&gt;{bypassCustomProtocolHandlers: true}&lt;/code&gt; option. This is just in case I have other protocol handlers in my app for the &lt;code&gt;&amp;quot;file://&amp;quot;&lt;/code&gt; scheme. If I don&amp;rsquo;t bypass, then my other custom protocol handlers will intercept this new request. Took me a whole day to figure that one out.&lt;/p&gt;
&lt;p&gt;And the code for the &lt;code&gt;AssetUrl&lt;/code&gt; class.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;AssetUrl&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;URL&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URL&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isNodeModule() {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;startsWith&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/node_modules&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;relativeUrl() {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isNodeModule&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/node_modules/&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pathname&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;replace&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;/^\//&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now depending on how you are serving your HTML file, you may see this error below in the dev tools.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Failed to construct &amp;#39;Worker&amp;#39;: Script at &amp;#39;app-asset://node_modules/monaco-editor/min/vs/base/worker/workerMain.js#editorWorkerService&amp;#39; cannot be accessed from origin &amp;#39;http://localhost:4567&amp;#39;.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I was serving my HTML files from the NextJS development server on port 4567. This is my code that loads the HTML in electron.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;env&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;isDevelopment&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`http://localhost:4567&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;?id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;amp;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`app-asset://zui&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;path&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;.html?id=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;amp;name=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;browserWindow&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;loadURL&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The reason for the error is the &amp;ldquo;same-origin&amp;rdquo; policy enforced by the browser window. This means that JavaScript can&amp;rsquo;t load more JavaScript from a different site. In my setup, the monaco code was trying to fetch a file at the &lt;code&gt;app-asset://zui&lt;/code&gt; origin, but it was running on the &lt;code&gt;http://localhost:4567&lt;/code&gt; origin.&lt;/p&gt;
&lt;p&gt;As you can see above, in production, the html file is also loaded from the &lt;code&gt;app-asset://zui&lt;/code&gt; origin so this error will not occur.&lt;/p&gt;
&lt;p&gt;To fix it in development, I decided to disable the same-origin check on the browser window, by passing &lt;code&gt;false&lt;/code&gt; to the &lt;code&gt;webSecurity&lt;/code&gt; option in &lt;a href=&#34;https://www.electronjs.org/docs/latest/api/structures/web-preferences&#34;&gt;webPreferences&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;browserWindow&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BrowserWindow&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;webPreferences&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;webSecurity&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;env.isProduction&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That &lt;code&gt;env&lt;/code&gt; object is a utility module I created to check for environment variables like &lt;code&gt;process.NODE_ENV === &amp;quot;production&amp;quot;&lt;/code&gt; .&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s all folks. I am happy with the resulting code and now the app works offline. What a concept.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Transitions All Settled</title>
      <link>https://www.jameskerr.blog/posts/all-transitions-settled/</link>
      <pubDate>Tue, 05 Mar 2024 20:26:00 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/all-transitions-settled/</guid>
      <description>&lt;p&gt;I just published a JavaScript library called &lt;a href=&#34;https://github.com/jameskerr/transitions-all-settled&#34;&gt;transitions-all-settled&lt;/a&gt; that allows you to wait for CSS transitions to settle before you do something.&lt;/p&gt;
&lt;p&gt;The package exports a single function that accepts an HTML node and returns a promise. The promise resolves when all CSS transitions on the HTML node and its children have settled.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;transitionsAllSettled&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;transitions-all-settled&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;transitionsAllSettled&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The term &amp;ldquo;settled&amp;rdquo; means that if a node received a &lt;code&gt;transitionstart&lt;/code&gt; event, it
also received a &lt;code&gt;transitionend&lt;/code&gt; or &lt;code&gt;transitioncancel&lt;/code&gt; event.&lt;/p&gt;
&lt;p&gt;This is useful for exit animations to wait for all CSS transitions to settle before removing the node from the DOM.&lt;/p&gt;
&lt;p&gt;Here is a demo. Click any square.&lt;/p&gt;
&lt;div id=&#34;canvas&#34; class=&#34;flow&#34;&gt;
    &lt;div class=&#34;square first&#34;&gt;&lt;/div&gt;
    &lt;div class=&#34;square second&#34;&gt;&lt;/div&gt;
    &lt;div class=&#34;square third&#34;&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;rsquo;s the code that makes that happen. Here&amp;rsquo;s the HTML.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;canvas&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;flow&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;square first&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;square second&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;class&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;square third&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The CSS.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;square&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;width&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;step&lt;span style=&#34;color:#ae81ff&#34;&gt;-3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;height&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;step&lt;span style=&#34;color:#ae81ff&#34;&gt;-3&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;text&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;transition-duration&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;s&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;clicked&lt;/span&gt; .&lt;span style=&#34;color:#a6e22e&#34;&gt;square&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;background-color&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;var&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;--&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;color&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;primary);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;transform&lt;/span&gt;: translateX(&lt;span style=&#34;color:#ae81ff&#34;&gt;100&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;%&lt;/span&gt;) rotate(&lt;span style=&#34;color:#ae81ff&#34;&gt;90&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;deg&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;first&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;transition-delay&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;second&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;transition-delay&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;500&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ms&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;third&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;transition-delay&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;ms&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the JavaScript.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; { &lt;span style=&#34;color:#a6e22e&#34;&gt;transitionsAllSettled&lt;/span&gt; } &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;transitions-all-settled&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;canvas&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; document.&lt;span style=&#34;color:#a6e22e&#34;&gt;getElementById&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;canvas&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;canvas&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;addEventListener&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;click&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;async&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;canvas&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;classList&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toggle&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;clicked&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;transitionsAllSettled&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;canvas&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;alert&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Transitions All Settled&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The package is very
small. Take a look at the source to see exactly how it works.&lt;/p&gt;
&lt;p&gt;GitHub: &lt;a href=&#34;https://github.com/jameskerr/transitions-all-settled&#34;&gt;jameskerr/transitions-all-settled&lt;/a&gt;&lt;/p&gt;
&lt;style&gt;
  .square {
    width: var(--step-3);
    height: var(--step-3);
    background-color: var(--color-text);
    transition-duration: 1s;
  }
  .clicked .square {
    background-color: var(--color-primary);
    transform: translateX(100%) rotate(90deg);
  }
  .first {
    transition-delay: 0;
  }
  .second {
    transition-delay: 500ms;
  }
  .third {
    transition-delay: 1000ms;
  }
&lt;/style&gt;
&lt;script type=&#34;module&#34;&gt;
  import { transitionsAllSettled } from &#34;https://esm.run/transitions-all-settled@0.2.0&#34;;

  const canvas = document.getElementById(&#34;canvas&#34;);
  canvas.addEventListener(&#34;click&#34;, async () =&gt; {
    canvas.classList.toggle(&#34;clicked&#34;);
    await transitionsAllSettled(canvas);
    alert(&#34;Transitions All Settled&#34;);
  });
&lt;/script&gt;
</description>
    </item>
    
    <item>
      <title>There Are Only Two Types of Transactions</title>
      <link>https://www.jameskerr.blog/posts/there-are-only-two-types-of-transactions/</link>
      <pubDate>Wed, 08 Nov 2023 09:40:46 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/there-are-only-two-types-of-transactions/</guid>
      <description>&lt;p&gt;&lt;span class=&#34;initial-cap&#34;&gt;There are&lt;/span&gt;
 only two types of transactions.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The ones you are present for.&lt;/li&gt;
&lt;li&gt;The ones you are not.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This just dawned on me. It&amp;rsquo;s easy to keep track of the spending you are present for (groceries, shopping, fun). It&amp;rsquo;s hard to keep track of the ones that happen in the background (rent, insurance, subscriptions). Even harder to try and manage both in your head!&lt;/p&gt;
&lt;p&gt;Ideally, you have a tool that tracks all background transactions for the month/pay-period and gives you the total. Subtract that from your income and you have &lt;strong&gt;the only number you need to keep track of&lt;/strong&gt;. The amount you are able to spend in-person. Type #1.&lt;/p&gt;
&lt;p&gt;Income - Background Transactions = Remaining for In-Person Transactions&lt;/p&gt;
&lt;p&gt;Remember when you were a kid and got 40 bucks? It was easy to keep track of it because there were no background transactions on that $40. You were present for every transaction. We&amp;rsquo;ve got to get back to that.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Against Single Element React Components</title>
      <link>https://www.jameskerr.blog/posts/against-single-element-react-components/</link>
      <pubDate>Mon, 06 Nov 2023 15:51:23 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/against-single-element-react-components/</guid>
      <description>&lt;p&gt;&lt;span class=&#34;initial-cap&#34;&gt; It&#39;s not uncommon &lt;/span&gt;
 to see this type of single element React component.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Title&lt;/span&gt;({ &lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;, ...&lt;span style=&#34;color:#a6e22e&#34;&gt;rest&lt;/span&gt; }) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;...rest&lt;/span&gt;}&amp;gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;}&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;h1&lt;/span&gt;&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you use a library like &lt;a href=&#34;https://styled-components.com/&#34;&gt;styled-components&lt;/a&gt;, you&amp;rsquo;re very much encouraged to write in this way.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Nav&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;styled&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;nav&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;``&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;List&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;styled&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ul&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;``&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Item&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;styled&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;li&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;``&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Link&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;styled&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;a&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;``&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyApp&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Nav&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;List&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Item&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Link&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/home&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Home&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Link&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Item&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Item&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Link&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/blog&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Blog&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Link&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Item&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Item&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Link&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/about&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;About&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Link&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Item&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;List&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Nav&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;problems-with-single-element-components&#34;&gt;Problems with Single Element Components&lt;/h2&gt;
&lt;p&gt;I have written my share of the code above. I won&amp;rsquo;t anymore.&lt;/p&gt;
&lt;p&gt;Take a look at that &lt;code&gt;&amp;lt;Link /&amp;gt;&lt;/code&gt; component. What props does it take? Where is the documentation for it? Well, just scroll up and see that it&amp;rsquo;s actually just an anchor tag. What if it&amp;rsquo;s exported from another file? Ok, just open up the file. Now what about every other component written this way? I&amp;rsquo;m going to be scrolling and opening all sorts of files to see what these tiny components do until I memorize them for my specific app.&lt;/p&gt;
&lt;p&gt;Now take a look at this code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No scrolling needed. No lookups needed. We all know &lt;em&gt;exactly&lt;/em&gt; what an anchor tag does. And if we don&amp;rsquo;t remember something, there are loads of docs on this and every other HTML element.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;ve all put countless hours in learning HTML. Hiding the underlying element with a tiny React component throws all that knowledge away.&lt;/p&gt;
&lt;h2 id=&#34;components-that-slightly-change-the-api&#34;&gt;Components that Slightly Change the API&lt;/h2&gt;
&lt;p&gt;To make this worse, some components slightly change the established HTML API. I see it often with buttons.&lt;/p&gt;
&lt;p&gt;We all know this API.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&amp;gt;Click Me&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But many times I&amp;rsquo;ve wrapped this up into something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isPrimary&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Click Me&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now everyone needs to memorize or lookup the new API whenever they need a button. Hard.&lt;/p&gt;
&lt;h2 id=&#34;the-solution-is-html-with-a-class-attribute&#34;&gt;The Solution is HTML with a Class Attribute&lt;/h2&gt;
&lt;p&gt;When we use the single element component pattern, we give up the well-known HTML APIs to encapsulate some visual styles. Not a fair trade. Especially when HTML has a built-in, stone-age solution for encapsulating visual styles: &lt;strong&gt;the class attribute&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;styles&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;./app.module.css&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyApp&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;nav&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;className&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;styles&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;mainNav&lt;/span&gt;}&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/home&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Home&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/blog&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Blog&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;href&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;/about&amp;#34;&lt;/span&gt;&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;About&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;a&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;li&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;nav&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nobody is going to wonder what&amp;rsquo;s going on here ☝️.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The leaf nodes of our component trees should be full of good &amp;lsquo;ol HTML because it is universally known and documented. Don&amp;rsquo;t throw all that out just to wrap up a visual style.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to Optimize Your Hugo Blogging Workflow on Mac</title>
      <link>https://www.jameskerr.blog/posts/how-to-optimize-your-hugo-blogging-workflow-on-mac/</link>
      <pubDate>Fri, 27 Oct 2023 09:22:36 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/how-to-optimize-your-hugo-blogging-workflow-on-mac/</guid>
      <description>&lt;p&gt;&lt;span class=&#34;initial-cap&#34;&gt;I love&lt;/span&gt;
 this quote by John Cutler from his post on &lt;a href=&#34;https://cutlefish.substack.com/p/tbm-3752-disincentives&#34;&gt;disincentives&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Good things can happen when you make it easier to do good things.&amp;rdquo; &amp;ndash;John Cutler&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He advises to focus less on incentivizing desired behavior, and more on removing disincentives from desired behavior. It&amp;rsquo;s brilliant.&lt;/p&gt;
&lt;p&gt;My desired behavior is to write more posts on my &lt;a href=&#34;https://gohugo.io/&#34;&gt;Hugo&lt;/a&gt; blog.&lt;/p&gt;
&lt;p&gt;I like Hugo because it&amp;rsquo;s free, fast, and fun to develop. But look at all these hurdles I had to jump when publishing!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a terminal&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;hugo new posts/my-interesting-post&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remember 👆 syntax since I don&amp;rsquo;t do it frequently&lt;/li&gt;
&lt;li&gt;Open the new file in a markdown editor&lt;/li&gt;
&lt;li&gt;🌟 WRITE THE WORDS (desired behavior)&lt;/li&gt;
&lt;li&gt;Back to the terminal&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;hugo serve&lt;/code&gt; to preview&lt;/li&gt;
&lt;li&gt;Open up the browser&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;localhost:1313&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Back to the terminal to &lt;code&gt;git add .&lt;/code&gt; &lt;code&gt;git push&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Thankfully, Netlify will automatically deploy on push&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&amp;rsquo;s how I removed all that friction.&lt;/p&gt;
&lt;h2 id=&#34;macos-shortcuts&#34;&gt;macOS Shortcuts&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&#34;shortcuts.png&#34; height=&#34;120px&#34;/&gt;
&lt;/figure&gt;

&lt;p&gt;I used the macOS Shortcuts app to automate the steps above. Then I put the shortcut icons in my dock for a &amp;ldquo;one-click&amp;rdquo; experience.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s my new workflow.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;One click to start writing a new post.&lt;br&gt;
&lt;figure&gt;&lt;img src=&#34;new-post-button.png&#34; height=&#34;120px&#34;/&gt;
   &lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;One click to preview it a browser.&lt;br&gt;
&lt;figure&gt;&lt;img src=&#34;preview-button.png&#34; height=&#34;120px&#34;/&gt;
   &lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;One click to publish.&lt;br&gt;
&lt;figure&gt;&lt;img src=&#34;publish-button.png&#34; height=&#34;120px&#34;/&gt;
   &lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;it-worked&#34;&gt;It Worked!&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m writing much more now that I&amp;rsquo;ve got these buttons. Here are screenshots showing you how to set these up for yourself.&lt;/p&gt;
&lt;h2 id=&#34;1-new-post-bundle-shortcut&#34;&gt;1. &amp;lsquo;New Post Bundle&amp;rsquo; Shortcut&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&#34;new-post-shortcut.png&#34;/&gt;
&lt;/figure&gt;

&lt;div class=&#34;note flow&#34;&gt;
  &lt;h6&gt;What&amp;#39;s going on here?&lt;/h6&gt;
  &lt;ol&gt;
&lt;li&gt;The first action prompts me for the title text&lt;/li&gt;
&lt;li&gt;Then the second action has my response in the $1 variable&lt;/li&gt;
&lt;li&gt;Change directory in my blog&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;hugo new posts/$1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Open my markdown editor iA Writer&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice I had to write the full path to the &lt;code&gt;hugo&lt;/code&gt; binary. Not sure why.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&#34;2-preview-blog-shortcut&#34;&gt;2. &amp;lsquo;Preview Blog&amp;rsquo; Shortcut&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&#34;preview-shortcut.png&#34;/&gt;
&lt;/figure&gt;

&lt;div class=&#34;note flow&#34;&gt;
  &lt;h6&gt;What&amp;#39;s going on here?&lt;/h6&gt;
  &lt;ol&gt;
&lt;li&gt;First I kill all exisiting &lt;code&gt;hugo&lt;/code&gt; processes&lt;/li&gt;
&lt;li&gt;Change directory into the blog&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;hugo serve &amp;amp;&lt;/code&gt; putting the server process in the background 4. Then I open localhost:1313 in the default browser&lt;/li&gt;
&lt;/ol&gt;

&lt;/div&gt;

&lt;h2 id=&#34;3-publish-blog-shortcut&#34;&gt;3. &amp;lsquo;Publish Blog&amp;rsquo; Shortcut&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&#34;publish-shortcut.png&#34;/&gt;
&lt;/figure&gt;

&lt;div class=&#34;note flow&#34;&gt;
  &lt;h6&gt;What&amp;#39;s going on here?&lt;/h6&gt;
  &lt;ol&gt;
&lt;li&gt;Changes directory into blog&lt;/li&gt;
&lt;li&gt;Git add everything&lt;/li&gt;
&lt;li&gt;Push it to main&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Is there an app that makes confetti fall from the top of the screen? That&amp;rsquo;s the missing piece of this shortcut.&lt;/p&gt;
&lt;/div&gt;

&lt;h2 id=&#34;video-demo&#34;&gt;Video Demo&lt;/h2&gt;
&lt;p&gt;To see this all in action, here&amp;rsquo;s a video of me creating, writing, and publishing this exact post that you&amp;rsquo;re reading!&lt;/p&gt;

&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
  &lt;iframe src=&#34;https://www.youtube.com/embed/oolfqFOkXig&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; allowfullscreen title=&#34;YouTube Video&#34;&gt;&lt;/iframe&gt;
&lt;/div&gt;

</description>
    </item>
    
    <item>
      <title>A Better Goal Than Sinless Perfection</title>
      <link>https://www.jameskerr.blog/posts/a-better-goal-than-sinless-perfection/</link>
      <pubDate>Sun, 22 Oct 2023 13:46:13 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/a-better-goal-than-sinless-perfection/</guid>
      <description>&lt;p&gt;Any two people in a close relationship are bound to eventually hurt each other. To most of us, it is no surprise when this happens. It’s simply accepted as the cost of growing close.&lt;/p&gt;
&lt;p&gt;Even though it feels terrible to be hurt, it would be a pointless endeavor to define success in the relationship as perfectly avoiding ever hurting the other’s feelings.&lt;/p&gt;
&lt;p&gt;Instead, I would say success is using the conflict and the wounds to grow into a bigger, deeper person and to strengthen the relationship by repairing the tears. The cycle of wounding and healing results in strength and resilience. It’s true in relationships, our bodies, our minds, and our natural world.&lt;/p&gt;
&lt;p&gt;However, with this knowledge and a desire for optimal efficiency, I might attempt a shortcut to growth by intentionally inflicting the wounds. This will not work. It does not pass the any common sense check. Instead, I wait. The wounds and the hurt will inevitably come. They are always painful but they are also expected.&lt;/p&gt;
&lt;p&gt;This is a wonderful way for me think about sin and God. The goal is not to perfectly avoid sinning. It is not a rare, shameful, embarrassing thing and it is not surprising when it happens. It’s painful, but expected. And when it occurs, the perfect forgiveness of God draws me into new depths of being. I use the sin and the forgiveness, the wound and the repair to humbly grow into the best versions of myself. I learn to forgive myself and everyone else from God’s example.&lt;/p&gt;
&lt;p&gt;Therefore, let me change the goal from being perfectly sinless to becoming a deep, humble, forgiving human. Let me use my inevitable sin and God&amp;rsquo;s complete forgiveness to get there.&lt;/p&gt;
&lt;p&gt;Progress, not perfection.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>I Have Enough</title>
      <link>https://www.jameskerr.blog/posts/i-have-enough/</link>
      <pubDate>Tue, 17 Oct 2023 09:28:23 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/i-have-enough/</guid>
      <description>&lt;div class=&#34;poem&#34;&gt;&lt;p&gt;I have enough food in my kitchen.&lt;/p&gt;
&lt;p&gt;I have enough water to drink.&lt;/p&gt;
&lt;p&gt;I have enough clothes to wear.&lt;/p&gt;
&lt;p&gt;I have enough things in my house.&lt;/p&gt;
&lt;p&gt;I have enough skill to work.&lt;/p&gt;
&lt;p&gt;I have enough ideas to think.&lt;/p&gt;
&lt;p&gt;I have enough people that love me.&lt;/p&gt;
&lt;p&gt;I have enough people to love.&lt;/p&gt;
&lt;p&gt;I have enough people to talk to.&lt;/p&gt;
&lt;p&gt;I have enough people to laugh with.&lt;/p&gt;
&lt;p&gt;I have enough time to relax.&lt;/p&gt;
&lt;p&gt;I have enough life to experience.&lt;/p&gt;
&lt;p&gt;I have enough.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Enough.&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;A quote from my from my pastor last week at church has been rolling around in my head all this week.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Money is a useful tool for moving things around, so that everyone can have what they need.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It made me think of &amp;ldquo;everything I need.&amp;rdquo; Truth is, I have too many things. I don&amp;rsquo;t want more things around me. Yet, I always wish I had more money. For what? I was then inspired to think of all the ways I have enough and write it up in a poem.&lt;/p&gt;
&lt;p&gt;What a liberating thing to realize.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Company Culture &amp; Church Community</title>
      <link>https://www.jameskerr.blog/posts/company-culture-church-community/</link>
      <pubDate>Thu, 12 Oct 2023 11:06:12 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/company-culture-church-community/</guid>
      <description>&lt;p&gt;This is a great &lt;a href=&#34;https://twitter.com/37signals/status/1712039586442936350&#34;&gt;podcast episode&lt;/a&gt; about company culture. The gist is, it doesn&amp;rsquo;t matter what people say or write about what the culture is. It&amp;rsquo;s what people are actually doing that matters. In fact, it may be even worse if what is said about the culture doesn&amp;rsquo;t match what people are actually doing.&lt;/p&gt;
&lt;p&gt;There was one sound-bite that stood out to me from DHH at minute 7:15.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The more companies are eager to talk, talk, talk, about culture, culture, culture all the time, the more suspicious I am that there is a bunch of bullshit hidden under the carpet. &amp;hellip; If you are obsessed with talking about culture, writing it down, it&amp;rsquo;s probably because you&amp;rsquo;re trying to cover something up.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then it hit me. This is exactly how I feel when churches talk, talk, talk about &amp;ldquo;community&amp;rdquo;. In fact, if you listen to the beginning of this podcast and replace every time they say &amp;ldquo;company&amp;rdquo; with &amp;ldquo;church&amp;rdquo; and &amp;ldquo;culture&amp;rdquo; with &amp;ldquo;community&amp;rdquo;, it would be spot on.&lt;/p&gt;
&lt;p&gt;There is a lesson here for us. If churches spend less time talking about community and more time simply doing things together, an authentic community will form naturally. And the &lt;em&gt;actions&lt;/em&gt; of the leaders in the church, much more than the words, will be the blueprint for the rest of the congregation.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Let It Flow</title>
      <link>https://www.jameskerr.blog/posts/let-it-flow/</link>
      <pubDate>Mon, 02 Oct 2023 12:02:50 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/let-it-flow/</guid>
      <description>&lt;p&gt;I was just in the gym and as I lifted a weight I thought of the energy flowing out of me to lift it. I thought, it&amp;rsquo;s good for me to get this energy out. It feels good and my body is healthier when I send the energy out into the weight.&lt;/p&gt;
&lt;p&gt;Then I exhaled. That air was also flowing out of me. It was good for it to go out of me. If I held it in, I would be poisoned. It needed to leave for me to live.&lt;/p&gt;
&lt;p&gt;Then I thought about my work. I have these words, ideas, and products that cry out for extraction.&lt;/p&gt;
&lt;p&gt;And money. What is money if it doesn&amp;rsquo;t leave me? It&amp;rsquo;s nothing. Useless paper.&lt;/p&gt;
&lt;p&gt;Then there is my love. It too must leave. Out of me and into others.&lt;/p&gt;
&lt;p&gt;Of course, there is time. It&amp;rsquo;s always leaving me. Coming and going. Moving. Flowing. Starting and ending and starting forever.&lt;/p&gt;
&lt;p&gt;And music, and sports, and art, and food, and relationships, and kids, and feelings, and God. They all must flow in and flow out.&lt;/p&gt;
&lt;p&gt;It seems that, to achieve fulfillment, everything must come out of me. To hold on to anything is to poison myself. The whole gift of being that was given at my conception must flow out from my soul and into Life.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s for my joy to fully exhale all that God breaths into me. Let it flow.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to Pipe a NodeJS Readable Stream into a Child Process</title>
      <link>https://www.jameskerr.blog/posts/pipe-nodejs-readable-stream-into-child-process/</link>
      <pubDate>Fri, 22 Sep 2023 10:00:27 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/pipe-nodejs-readable-stream-into-child-process/</guid>
      <description>&lt;p&gt;I love pipes. Anytime I can pipe something somewhere, it seems that order has claimed a victory over chaos. In fact, there is pipe-related photo of me at the end of this article for your viewing pleasure.&lt;/p&gt;
&lt;p&gt;The other day I was working in NodeJS and wanted to pipe a readable stream to a spawned child process.&lt;/p&gt;
&lt;p&gt;I wanted something that could do this.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createProcess&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;zq&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createTransformStream&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;input&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;pipe&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;zq&lt;/span&gt;); &lt;span style=&#34;color:#75715e&#34;&gt;// &amp;lt;-- Very cool
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;client&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;load&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The child process needed to be a wrapped in a transform stream that would feed data to stdin and pass on data from stdout.&lt;/p&gt;
&lt;h2 id=&#34;spawn-a-child-process&#34;&gt;Spawn a Child Process&lt;/h2&gt;
&lt;p&gt;First, I spawned my process. NodeJS provides the &lt;a href=&#34;https://nodejs.org/api/child_process.html#child_processspawncommand-args-options&#34;&gt;spawn&lt;/a&gt; function to fire up an executable on your file system. The return value is a &lt;a href=&#34;https://nodejs.org/api/child_process.html#class-childprocess&#34;&gt;ChildProcess&lt;/a&gt; object.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createProcess&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// Massage your args for the binary you&amp;#39;re using.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;spawn&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;bin&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;spawnargs&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;wrap-the-child-process-in-a-transform-stream&#34;&gt;Wrap the Child Process in a Transform Stream&lt;/h2&gt;
&lt;p&gt;Now we need to wrap that process in a &lt;a href=&#34;https://nodejs.org/api/stream.html#class-streamtransform&#34;&gt;transform stream&lt;/a&gt; so that we can pipe, pipe, pipe.&lt;/p&gt;
&lt;p&gt;The steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Receive a chunk of data as an argument in the &lt;code&gt;transform&lt;/code&gt; function.&lt;/li&gt;
&lt;li&gt;Write that chunk to the child&amp;rsquo;s &lt;code&gt;stdin&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;Receive data coming from the child&amp;rsquo;s &lt;code&gt;stdout&lt;/code&gt; .&lt;/li&gt;
&lt;li&gt;Push that data into the transform stream.&lt;/li&gt;
&lt;li&gt;Handle errors and clean up.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is the code.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;createTransformStream&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Transform&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;transform&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;chunk&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;encoding&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;chunk&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;encoding&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;nextTick&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;once&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;drain&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;flush&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdout&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroyed&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdout&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;close&amp;#34;&lt;/span&gt;, () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EPIPE&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#75715e&#34;&gt;// finished before reading the file finished (i.e. head)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;emit&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;end&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stderr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Error(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;())))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before we go into detail about what this code does, let&amp;rsquo;s discuss a very confusing topic for me, NodeJS streams.&lt;/p&gt;
&lt;h2 id=&#34;understanding-nodejs-readables&#34;&gt;Understanding NodeJS Readables&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&#34;https://nodejs.org/api/stream.html#readable-streams&#34;&gt;readable&lt;/a&gt; is like a file. Call &lt;code&gt;readable.read()&lt;/code&gt; to get the first chunk of data from the file.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;chunk&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;readable&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;read&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But if I am creating my own readable, it starts off empty. There is no data to read. To add some, use the &lt;code&gt;readable.push()&lt;/code&gt; method.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;readable&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;my-chunk-of-data&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This was confusing to me, because I am essentially &amp;ldquo;writing&amp;rdquo; data into the readable. But don&amp;rsquo;t say it like that, because the &lt;code&gt;write()&lt;/code&gt; method name is already taken as we&amp;rsquo;ll see next.&lt;/p&gt;
&lt;h2 id=&#34;understanding-nodejs-writables&#34;&gt;Understanding NodeJS Writables&lt;/h2&gt;
&lt;p&gt;A &lt;a href=&#34;https://nodejs.org/api/stream.html#writable-streams&#34;&gt;writable&lt;/a&gt; is a destination for data to land. The writable thing takes the data I give it with &lt;code&gt;writable.write()&lt;/code&gt; and does something with it. To indicate that I have written all the data I have to it, I call &lt;code&gt;writable.end()&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;writable&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;first chunk&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;writable&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;second chunk&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;writable&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;ok, i&amp;#39;m done&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;writable&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;understanding-nodejs-duplex-streams&#34;&gt;Understanding NodeJS Duplex Streams&lt;/h2&gt;
&lt;p&gt;To make everything super confusing, some objects can be both &lt;a href=&#34;https://nodejs.org/api/stream.html#duplex-and-transform-streams&#34;&gt;readable and writable&lt;/a&gt;. This means I can call &lt;code&gt;.push()&lt;/code&gt;, &lt;code&gt;.read()&lt;/code&gt;, &lt;code&gt;.write()&lt;/code&gt;, and &lt;code&gt;.end()&lt;/code&gt; on these things.&lt;/p&gt;
&lt;p&gt;A special type of duplex stream is called the &lt;a href=&#34;https://nodejs.org/api/stream.html#class-streamtransform&#34;&gt;transform stream&lt;/a&gt;. It provides a shorthand way of reading from a source and writing to a destination. That&amp;rsquo;s what I used in the code above.&lt;/p&gt;
&lt;h2 id=&#34;detailed-code-breakdown&#34;&gt;Detailed Code Breakdown&lt;/h2&gt;
&lt;p&gt;First we create the transform stream which will be the return value. We implement two methods in the constructor, &lt;code&gt;transform()&lt;/code&gt; and &lt;code&gt;flush()&lt;/code&gt;. The first is called when a chunk of data is read from a source, the second is called when there&amp;rsquo;s no more data to read.&lt;/p&gt;
&lt;h3 id=&#34;the-transform-function&#34;&gt;The Transform Function&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;transform&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;chunk&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;encoding&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;write&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;chunk&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;encoding&lt;/span&gt;)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;process&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;nextTick&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;once&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;drain&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;a href=&#34;https://nodejs.org/api/stream.html#transform_transformchunk-encoding-callback&#34;&gt;transform function&lt;/a&gt; has the arguments &lt;code&gt;chunk&lt;/code&gt;, &lt;code&gt;encoding&lt;/code&gt;, and &lt;code&gt;callback&lt;/code&gt;. The &lt;code&gt;chunk&lt;/code&gt; is the bit of data that was just read and the &lt;code&gt;callback&lt;/code&gt; is supposed to be called after I&amp;rsquo;ve processed it.&lt;/p&gt;
&lt;p&gt;I pass that bit of data to my child process by writing to the process &lt;code&gt;stdin&lt;/code&gt;. If &lt;code&gt;stdin.write()&lt;/code&gt; returns true, it&amp;rsquo;s ready to accept more data so I call the &lt;code&gt;callback&lt;/code&gt; on the next tick. If it returns false, it wants me to wait for the &lt;code&gt;&amp;quot;drain&amp;quot;&lt;/code&gt; event before continuing, so we call the &lt;code&gt;callback&lt;/code&gt; once that event is fired. This is called &amp;ldquo;respecting back-pressure.&amp;rdquo; Respect.&lt;/p&gt;
&lt;h3 id=&#34;the-flush-function&#34;&gt;The Flush Function&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;flush&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdout&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroyed&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdout&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;close&amp;#39;&lt;/span&gt;, () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;callback&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;a href=&#34;https://nodejs.org/api/stream.html#transform_flushcallback&#34;&gt;flush function&lt;/a&gt; is called when the stream has finished reading the source. It has one &lt;code&gt;callback&lt;/code&gt; argument that should be called when I&amp;rsquo;ve cleaned everything up. In the body, I tell the child process&amp;rsquo; &lt;code&gt;stdin&lt;/code&gt; that I will no longer write any more data. Then I wait for the child process&amp;rsquo; &lt;code&gt;stdout&lt;/code&gt; to close before calling the &lt;code&gt;callback&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;listening-to-stdout&#34;&gt;Listening to stdout&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;push&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is where I &amp;ldquo;push&amp;rdquo; the data that comes out of my child process into the transform stream. If there&amp;rsquo;s an error, I call &lt;a href=&#34;https://nodejs.org/api/stream.html#writabledestroyerror&#34;&gt;destroy&lt;/a&gt; and pass in the error.&lt;/p&gt;
&lt;h3 id=&#34;listening-to-stderr&#34;&gt;Listening to stderr&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stderr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;data&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; Error(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;())))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  .&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is some error handling. In my case, if anything gets pushed into &lt;code&gt;stderr&lt;/code&gt;, I consider it an error and destroy the transform stream providing the appropriate error text.&lt;/p&gt;
&lt;h3 id=&#34;listening-to-stdin&#34;&gt;Listening to stdin&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;child&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;stdin&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;on&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;error&amp;#34;&lt;/span&gt;, (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;EPIPE&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// the process finished before reading the file finished
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;emit&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;end&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;stream&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;destroy&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;More error handling. Sometimes the child process would finish before I had given it all of the file. (The case where I only want the first 5 lines of a long file.) I write to &lt;code&gt;stdin&lt;/code&gt;, but it&amp;rsquo;s closed up and emits the error code &lt;code&gt;&amp;quot;EPIPE&amp;quot;&lt;/code&gt;. I handle that by emitting the &lt;code&gt;&amp;quot;end&amp;quot;&lt;/code&gt; event on the transform stream. This was the only way I could get it to work. I tried calling &lt;code&gt;.end()&lt;/code&gt; but that didn&amp;rsquo;t cut it. I had to emit the event manually.&lt;/p&gt;
&lt;p&gt;If the error code is anything else, I destroy the stream like above.&lt;/p&gt;
&lt;h2 id=&#34;articleend&#34;&gt;article.end()&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s is how I wrapped a NodeJS ChildProcess with a Stream.Transform object so that I can pipe data to and from it. I hope this saves you some time so that you can get back to your pipes.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s me with my pipes in 2015.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;./bagpipes.jpeg&#34;&gt;&lt;img src=&#34;bagpipes.jpeg&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

</description>
    </item>
    
    <item>
      <title>Waiting for a Comet</title>
      <link>https://www.jameskerr.blog/posts/waiting-for-a-comet/</link>
      <pubDate>Wed, 20 Sep 2023 09:53:06 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/waiting-for-a-comet/</guid>
      <description>&lt;div class=&#34;poem&#34;&gt;&lt;p&gt;Comets are constantly falling.&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t know when.&lt;/p&gt;
&lt;p&gt;But if we&amp;rsquo;re open, our eyes are open,&lt;/p&gt;
&lt;p&gt;The comment will appear.&lt;/p&gt;
&lt;p&gt;It is not me.&lt;/p&gt;
&lt;p&gt;And it will change everything.&lt;/p&gt;
&lt;p&gt;If we&amp;rsquo;d only wait for it to appear.&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This poem was written on a backpacking trip through the Wind River Range in Wyoming. I slept under the stars. A meteor broke into pieces in the middle of the night and it seemed I was the only one in the audience. Later, the moon rose over the mountains. As I lied under the terrifying vastness of the space above me, I was moved to mutter, &amp;ldquo;I surrender to you God.&amp;rdquo; Then for some reason I thought, &amp;ldquo;I have children. Vanessa carried them for us.&amp;rdquo;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>A Battle of Wit</title>
      <link>https://www.jameskerr.blog/posts/a-battle-of-wit/</link>
      <pubDate>Sat, 16 Sep 2023 17:44:44 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/a-battle-of-wit/</guid>
      <description>&lt;div class=&#34;poem&#34;&gt;&lt;p&gt;Swords drawn, Draw blood, Blood in the water&lt;/p&gt;
&lt;p&gt;Recover, Respond, Respect&lt;/p&gt;
&lt;p&gt;Cross the line, Cut too deep, Feel the heat&lt;/p&gt;
&lt;p&gt;Reach out a hand, Pull up to stand&lt;/p&gt;
&lt;p&gt;Love wrapped up in a sheath&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;This was written on a backpacking trip where eight men hiked for four days in the wilderness. Whenever a group of friends are together, you&amp;rsquo;re bound to find this battlefield.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Partially Controlled Components: A Declarative Design Pattern in React</title>
      <link>https://www.jameskerr.blog/posts/partially-controlled-react-components/</link>
      <pubDate>Tue, 01 Aug 2023 12:00:00 -0800</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/partially-controlled-react-components/</guid>
      <description>&lt;p&gt;A common distinction in React is &lt;a href=&#34;https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components&#34;&gt;Controlled vs Uncontrolled&lt;/a&gt; components. But the real world is not so black and white&amp;hellip;&lt;/p&gt;
&lt;p&gt;To summarize:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Uncontrolled&lt;/strong&gt; components manage changes within themselves, &lt;em&gt;internally&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Controlled&lt;/strong&gt; components have their changes managed for them, from the outside, &lt;em&gt;externally&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I recently authored a tree view React component called &lt;a href=&#34;https://github.com/brimdata/react-arborist&#34;&gt;react-arborist&lt;/a&gt;. After working on a complex component like this I felt the absence of a necessary concept in our React community.&lt;/p&gt;
&lt;p&gt;✨ &lt;strong&gt;&lt;em&gt;Partially Controlled Components&lt;/em&gt;&lt;/strong&gt; ✨&#39;&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve felt it too? Let&amp;rsquo;s get into this.&lt;/p&gt;
&lt;h2 id=&#34;gradually-controllable-complex-components&#34;&gt;Gradually Controllable Complex Components&lt;/h2&gt;
&lt;p&gt;As a library author, I want the &amp;ldquo;Getting Started &amp;quot; experience to be dead simple.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Tree&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;initialData&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;myData&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, the component takes care of everything internally: selection, drag-and-drop, and renaming. As it integrates into an app, users can take control of &lt;strong&gt;only the parts they need&lt;/strong&gt;. Common needs are persisting the state, initializing the state, or changing the tree from elsewhere in the app.&lt;/p&gt;
&lt;h2 id=&#34;declarative-vs-imperative-control&#34;&gt;Declarative vs Imperative Control&lt;/h2&gt;
&lt;p&gt;There are only two ways to control a component from the outside:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Attaching a &lt;strong&gt;Ref&lt;/strong&gt; &lt;em&gt;(imperative)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Providing &lt;strong&gt;Props&lt;/strong&gt; &lt;em&gt;(declarative)&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&amp;rsquo;s look at some real world component libraries to showcase these two approaches.&lt;/p&gt;
&lt;h2 id=&#34;imperative-control-in-react-resizable-panes&#34;&gt;Imperative Control in react-resizable-panes&lt;/h2&gt;
&lt;p&gt;First we will examine &lt;a href=&#34;https://github.com/bvaughn/react-resizable-panels&#34;&gt;react-resizable-panes&lt;/a&gt;, a promising new React component library by &lt;a href=&#34;https://www.bvaughn.me&#34;&gt;Brian Vaughn&lt;/a&gt;. The Panel component is uncontrolled by default (win), and provides a ref handle to control it from the outside.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;import&lt;/span&gt; {&lt;span style=&#34;color:#a6e22e&#34;&gt;Panel&lt;/span&gt;} &lt;span style=&#34;color:#a6e22e&#34;&gt;from&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;react-resizable-panels&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyApp&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;onClick&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; () =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;collapse&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Panel&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;collapsible&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;}&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;Panel&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;onClick&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;onClick&lt;/span&gt;}&amp;gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;Collapse&lt;/span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can control the collapsible state from the outside by calling the &lt;code&gt;collapse()&lt;/code&gt; method on the Panel&amp;rsquo;s &lt;a href=&#34;https://react.dev/reference/react/useImperativeHandle&#34;&gt;imperative handle&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;declarative-control-in-tanstack-table&#34;&gt;Declarative Control in Tanstack Table&lt;/h2&gt;
&lt;p&gt;In the React version of &lt;a href=&#34;https://tanstack.com/table/v8&#34;&gt;tanstack-table&lt;/a&gt;, the go-to component library for everything tabular, &lt;a href=&#34;https://twitter.com/tannerlinsley&#34;&gt;Tanner Linsley&lt;/a&gt; allows us to partially control the state of our table by passing arguments to the &lt;a href=&#34;https://tanstack.com/table/v8/docs/adapters/react-table&#34;&gt;useReactTable&lt;/a&gt; hook.&lt;/p&gt;
&lt;p&gt;His library only provides a hook and leaves rendering for us, but this is essentially passing props to a component. When the arguments to the hook change, React will re-render our table.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useReactTable&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;columns&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;columnVisibility&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// visibility value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;columnOrder&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// order value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;onColumnVisibilityChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setColumnVisibility&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// visibility setter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;onColumnOrderChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setColumnOrder&lt;/span&gt;, &lt;span style=&#34;color:#75715e&#34;&gt;// order setter
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Look at that partial control! I really think he&amp;rsquo;s onto something. Since the component/hook does so many things, the state is divided into logical slices. Each slice can either be controlled or uncontrolled depending on the presence of an &lt;code&gt;on[SliceName]Change&lt;/code&gt; handler. I love this pattern. However, to me, its undesirable that the value and change handler are not co-located. It is also a bit verbose. But it is declarative and much more &amp;ldquo;React&amp;rdquo;.&lt;/p&gt;
&lt;h2 id=&#34;api-inspiration-from-react-input-elements&#34;&gt;API Inspiration from React Input Elements&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s get back to our roots. The basic React &lt;a href=&#34;https://react.dev/reference/react-dom/components/input&#34;&gt;&lt;code&gt;&amp;lt;input /&amp;gt;&lt;/code&gt;&lt;/a&gt; element is where the concept of controlled/uncontrolled components was born. The React authors decided on this API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;by default the input is uncontrolled&lt;/li&gt;
&lt;li&gt;by adding props, you opt-in to control&lt;/li&gt;
&lt;li&gt;The relevant prop names are &lt;code&gt;value&lt;/code&gt;, &lt;code&gt;defaultValue&lt;/code&gt;, and &lt;code&gt;onChange&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;1-uncontrolled&#34;&gt;1. Uncontrolled&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;2-uncontrolled-with-an-initial-value&#34;&gt;2. Uncontrolled with an initial value&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;defaultValue&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;3-controlled&#34;&gt;3. Controlled&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;} &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setValue&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;e&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;currentTarget&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;)} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;a-declarative-pattern-for-partially-controlled-components&#34;&gt;A Declarative Pattern for Partially Controlled Components&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s expand on all the patterns we saw above and design a new API for partially controlled components. We&amp;rsquo;ll use react-arborist as an example use case.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Case 1:&lt;/strong&gt; A user wants control over just the tree&amp;rsquo;s selection state.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;setSelection&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;initial&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Tree&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;selectionState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setSelection&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Use Case 2:&lt;/strong&gt; They want to control the editing state as well.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Tree&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;selectionState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setSelection&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;editingState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;editing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setEditing&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Use Case 3:&lt;/strong&gt; Provide only an initial state for which nodes are expanded/collapsed?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Tree&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;selectionState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setSelection&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;editingState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;editing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setEditing&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;expandedState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;defaultValue&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;expanded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Use Case 4:&lt;/strong&gt; Control all the state in a Redux store.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;treeState&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useSelector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;MyApp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;selectTreeState&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dispatch&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useDispatch&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Tree&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;rootState&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;treeState&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;onChange&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dispatch&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;MyApp&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;setTreeState&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;state&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each logical group of state has its own prop object for users to control. The presence of this prop indicates to the component that its state slice will be managed externally.&lt;/p&gt;
&lt;p&gt;I like this pattern for a few reasons.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s declarative&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s familiar&lt;/li&gt;
&lt;li&gt;Values and handlers are co-located&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s uncontrolled by default&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s easy to gradually take control&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s Next?&lt;/h2&gt;
&lt;p&gt;What do you think of this design? Depending on your feedback, I may make this the API in version 4 of react-arborist.&lt;/p&gt;
&lt;p&gt;Curious how one would implement an API like this? Keep a lookout for part two!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Others May Know More</title>
      <link>https://www.jameskerr.blog/posts/others-may-know-more/</link>
      <pubDate>Mon, 10 Jul 2023 04:13:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/others-may-know-more/</guid>
      <description>&lt;p&gt;A child knows nothing about itself. Its body is a mystery, its ideas, its dreams, its feelings. The child relies on caregivers to teach her everything. The caregivers know more about who she is than she does.&lt;/p&gt;
&lt;p&gt;Does this ever stop? Do any of us ever fully know who we are? No matter what age I am, will there be others who know more about me than I do?&lt;/p&gt;
&lt;p&gt;Maybe not me as James, but me as human being. Me as human spirit.&lt;/p&gt;
&lt;p&gt;I cannot assume that I know everything about what it means to be human and I must assume that others or God or scriptures can know more.&lt;/p&gt;
&lt;p&gt;What a humbling thought: &lt;em&gt;Others may know more about me than I do.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>It’s Good to Wonder About Mysteries</title>
      <link>https://www.jameskerr.blog/posts/its-good-to-wonder-about-mysteries/</link>
      <pubDate>Thu, 29 Jun 2023 10:15:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/its-good-to-wonder-about-mysteries/</guid>
      <description>&lt;p&gt;I was talking with a group of friends trying to come up with possible answers to unanswerable questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What happens when we die?&lt;/li&gt;
&lt;li&gt;How does the way we live affect things after our death?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We can never know the answers to these questions with certainty. They are fundamentally unknowable. So what is the point of spending so much energy on something you’ll never be able to fully answer?&lt;/p&gt;
&lt;p&gt;A friend provided a profound response. While we can’t be certain of any particular theory, the conclusions we make will change the way we our lives right now. For example, if you believe our choices today affect the next life, you’ll behave differently than if you didn’t.&lt;/p&gt;
&lt;p&gt;It seems as if we first need to decide what kind of person we want to become; how we hope to live. Then as we evaluate possible answers to these unknowable questions, we judge them by how much they enable us to become that person.&lt;/p&gt;
&lt;p&gt;This thought is backwards when compared to the primary pursuit of truth common in religious circles. But it might be more true than we realize.&lt;/p&gt;
&lt;p&gt;Let’s make this personal. Today, I want to become a more generous, confident, humble, strong, meek, realistic, hopeful, creative, life-enhancing human.&lt;/p&gt;
&lt;p&gt;If a tenant of my worldview doesn’t help me get closer to those goals, why keep believing it? Because it’s true? If it’s one of these unanswerable questions, how can we ever know with certainty?&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>React Hook for Avoiding Flash of Empty UI While Data Transitions</title>
      <link>https://www.jameskerr.blog/posts/use-data-transition/</link>
      <pubDate>Tue, 27 Jun 2023 11:55:19 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/use-data-transition/</guid>
      <description>&lt;p&gt;I&amp;rsquo;m working on a React app that has frequent data transitions. Each time a user submits a query, the state is reset as we wait for a response from the server. The new data can arrive quickly or slowly. If it arrives quickly, a flash of empty UI will render as the data is transitioning from the previous results to the current results.&lt;/p&gt;
&lt;h2 id=&#34;before&#34;&gt;Before&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;./before.gif&#34; alt=&#34;before&#34;&gt;&lt;/p&gt;
&lt;p&gt;Notice the flash of emptiness in the histogram.&lt;/p&gt;
&lt;p&gt;Even though it&amp;rsquo;s not that big of a deal, I want to avoid that. Let&amp;rsquo;s write a short hook that will do this for us called &lt;em&gt;useDataTransition&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;At a high level, we need to prevent child React components from seeing the new data in their props until the transition is finished (we have all the new data) or a timeout expires (the new data is taking a while to arrive).&lt;/p&gt;
&lt;h2 id=&#34;return-value&#34;&gt;Return Value&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s work backward starting with the return value. The hook should return the real data or a cache of the previous data. If we are &amp;ldquo;in transition&amp;rdquo; and the timeout has not expired, return the cached data, otherwise the real data.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;timeExpired&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;} &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;real&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;cache&#34;&gt;Cache&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s figure out when to cache. We must save the data before the transition starts. Let&amp;rsquo;s put this in an effect that runs when &lt;em&gt;inTransition&lt;/em&gt; changes or when the data changes. Only cache if &lt;em&gt;isTransition&lt;/em&gt; is false.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;useEffect&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}, [&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This always caches the data while the we are not transitioning. Since we don&amp;rsquo;t need to re-render when the cache changes, we can store it in a ref.&lt;/p&gt;
&lt;h2 id=&#34;timer&#34;&gt;Timer&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s set that &lt;code&gt;timeExpired&lt;/code&gt; variable. Whenever &lt;em&gt;isTransitioning&lt;/em&gt; changes to &lt;code&gt;true&lt;/code&gt;, start a timer. When that timer finishes, update the &lt;code&gt;timeExpired&lt;/code&gt; state.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;timeExpired&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeExpired&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;useEffect&lt;/span&gt;(() =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(() =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeExpired&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;dur&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeExpired&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; () =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clearTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}, [&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dur&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We do want to re-render when &lt;code&gt;timeExpired&lt;/code&gt; changes so we &lt;code&gt;useState&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;the-finished-code&#34;&gt;The Finished Code&lt;/h2&gt;
&lt;p&gt;Now to bring it all together with TypeScript types.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;export&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useDataTransition&lt;/span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;T&lt;/span&gt;&amp;gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;real&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;T&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;boolean&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;timeout&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; [&lt;span style=&#34;color:#a6e22e&#34;&gt;timeExpired&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeExpired&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useState&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;real&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;useEffect&lt;/span&gt;(() &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;) &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;real&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }, [&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;real&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;useEffect&lt;/span&gt;(() &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeout&lt;/span&gt;(() &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeExpired&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;timeout&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;setTimeExpired&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;clearTimeout&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }, [&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;timeout&lt;/span&gt;]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;inTransition&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;timeExpired&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;cache&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;real&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can use it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-tsx&#34; data-lang=&#34;tsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;HistogramContainer() {&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;isFetching&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useSelector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;getIsFetching&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;noDataYet&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useSelector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;getIsEmpty&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;realData&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useSelector&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;getRealData&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useDataTransition&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;realData&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;isFetching&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;noDataYet&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;300&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;Histogram&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;} /&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the example above, we consider the data transitioning if we are in the middle of fetching and if we have not yet received any results. If either of those change, the transition will be done and the real data displayed. If they remain true for longer than 300ms, the real data will again be displayed which probably means some loading UI.&lt;/p&gt;
&lt;h2 id=&#34;after&#34;&gt;After&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;./after.gif&#34; alt=&#34;after&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;post-script&#34;&gt;Post Script&lt;/h2&gt;
&lt;p&gt;At first, I put this logic in &amp;ldquo;thunks&amp;rdquo;; outside of React components. But because it was a UI issue, not part of business logic, it felt right to move it to the rendering layer.&lt;/p&gt;
&lt;p&gt;Also, my first implementation wasn&amp;rsquo;t working and I was struggling to figure out why. As a means to help me think clearly, I started writing this post and ended up with something that worked. Win, win!&lt;/p&gt;
&lt;p&gt;I wrote this hook while working on the &lt;a href=&#34;https://github.com/brimdata/zui&#34;&gt;Zui&lt;/a&gt; app by &lt;a href=&#34;https://www.brimdata.io&#34;&gt;Brim Data&lt;/a&gt;. Check us out if you ever need to work with heterogeneous datasets.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Confronting a Man at the Playground</title>
      <link>https://www.jameskerr.blog/posts/playground-confrontation/</link>
      <pubDate>Wed, 21 Jun 2023 12:00:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/playground-confrontation/</guid>
      <description>&lt;p&gt;While at a playground this weekend a man talked to my 3-year-old son in a way that upset me and I had to do something about it. I thought carefully about what was true for me, then walked over and told him.&lt;/p&gt;
&lt;p&gt;Earlier that evening I observed this man enthusiastically playing with an 18-month-old boy we&amp;rsquo;ll call JJ. He wasn&amp;rsquo;t the dad, maybe a friend or an uncle. He took little JJ down the big slide multiple times. At one point the boy&amp;rsquo;s dad walked over and stood at the bottom of the slide. The man decided JJ should go down on his own. &amp;ldquo;3-2-1 GO!&amp;rdquo;, but JJ shook his head and asked for one of them to go down with him. After failing to convince JJ it would be fun, the man pretended to go down with him and at the last minute, sent the boy down solo.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been there. I&amp;rsquo;m a former &amp;ldquo;slide-pusher&amp;rdquo; myself. It may even be motivated by good intention. We want to push our children to face their fears and build courage. But at a certain point, a child is simply not able or willing to try something. Respecting this is also good. It gives them a feeling of autonomy and control over their own life.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve given up on slide pushing and now wait (not easy) until the kid wants to go down. My son was formerly hesitant about the big slide, but now rips down it like it&amp;rsquo;s going out of style. He was ready when he was ready. He probably feels proud of himself and doesn&amp;rsquo;t have to worry about pleasing me on this front. That is good.&lt;/p&gt;
&lt;p&gt;Now for the incident. It&amp;rsquo;s 5pm and my son is tired. He&amp;rsquo;s playing on this thing that spins you around. It&amp;rsquo;s small. Two kids can spin on it, but kids often spin one at a time. JJ walks over to the spinner and wants a turn. The man is walking away to sit down with his friends when he notices JJ and my son about to get into some classic toddler conflict.&lt;/p&gt;
&lt;p&gt;He asks my son, &amp;ldquo;Will you share the spinner with JJ?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;My son shakes his head.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;C&amp;rsquo;mon, both of you can do it. It&amp;rsquo;ll be so fun!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;My son refuses again. &amp;ldquo;No.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Then the dude says, &amp;ldquo;You have to share!&amp;rdquo; and when my son still refuses he starts loudly repeating his message, &amp;ldquo;Share! SHARE!&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;My son breaks down crying and runs over to me.&lt;/p&gt;
&lt;p&gt;My wife was sitting on the bench near the friends and overheard him say to them as he sat down, &amp;ldquo;That kid&amp;rsquo;s a jerk.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;If you have some paternal energy and feel like you&amp;rsquo;re about to flip your shit, so was I.&lt;/p&gt;
&lt;p&gt;I had just finished reading the chapter in Jordan Peterson&amp;rsquo;s excellent book, &lt;em&gt;12 Rules of Life,&lt;/em&gt; called &amp;ldquo;Tell the Truth (or at least don&amp;rsquo;t lie).&amp;rdquo; He stresses the importance of telling the truth to yourself and the world. He gave some examples of times when it was uncomfortable, but had great benefits. When you decide to say something honest, his advice is to be concise, direct, and clear. Go slow and choose the words carefully.&lt;/p&gt;
&lt;p&gt;I was upset by the way the man spoke to my son. That was it. That was my truth.&lt;/p&gt;
&lt;p&gt;As a big fan of non-violent communication, I tried to perceive what the man was feeling in that moment. He was probably frustrated, because toddlers are frustrating, especially when trying to teach them to share. That was also true.&lt;/p&gt;
&lt;p&gt;As we were leaving the park, I decided it was time to speak. I walked over and sat next to him. He was in front of four other adults so my heart was pumping.&lt;/p&gt;
&lt;p&gt;I said, &amp;ldquo;Hey brother, I know that toddlers can be frustrating, but I was upset by the way you spoke to my son by the spinner thing.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;He responded, &amp;ldquo;Sorry about that. I was just trying to get him to share with our little JJ. Sorry about that.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I said thanks and we left.&lt;/p&gt;
&lt;p&gt;I am proud of myself. It was nerve-racking to do, but I had to do something and telling the truth seemed like a good option. Maybe he will learn something from that experience, but it was mostly for me. I want to practice that kind of thing. Maybe there&amp;rsquo;ll be a time where it&amp;rsquo;s harder to speak up, but even more necessary.&lt;/p&gt;
&lt;p&gt;Let me add this. The next day I was filled with anxiety. I kept wondering if I would run into that dude on the street. I was surprised by this. As I reflected on it, it seemed similar to my experiences in jr. high when other kids treated me poorly. There was a terrible feeling of powerlessness I felt then. If someone picked on me, I had no recourse. I wasn&amp;rsquo;t the fighting type, being a tattle-tail wasn&amp;rsquo;t cool, and I couldn&amp;rsquo;t chose not to be around them. Somehow this experience brought me back to those days. Who knows&amp;hellip;&lt;/p&gt;
&lt;p&gt;It did suck having to deal with all of this. It was an emotionally rigorous day and a half. But I got to practice telling the truth. Maybe next time it&amp;rsquo;ll be easier.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Persisting the Human Experience</title>
      <link>https://www.jameskerr.blog/posts/compressing-experience/</link>
      <pubDate>Mon, 19 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/compressing-experience/</guid>
      <description>&lt;p&gt;Experiences compress into memories.&lt;br&gt;
Memories compress into feelings.&lt;br&gt;
Feelings compress into instincts.&lt;br&gt;
Instincts compress into the unconscious.&lt;br&gt;
The unconscious compresses into the collective human unconscious.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s an analogy in software. When you make a song in music software, you start with big uncompressed files. All the sound layers are stored separately. When the song is finished, it gets flattened into one layer resulting in the same sounds, but a smaller file size. We can then compress that file even more with general compression algorithms. Finally, it can be uploaded to the cloud and streamed to millions of others without having to take up any of their disk space.&lt;/p&gt;
&lt;p&gt;Maybe that’s how the immesurable data from billions of experiences get mysteriously persisted into our human nature.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Joshua Tree Trip</title>
      <link>https://www.jameskerr.blog/posts/joshua-tree-trip/</link>
      <pubDate>Wed, 31 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/joshua-tree-trip/</guid>
      <description>&lt;p&gt;We took a trip down to Joshua Tree this weekend for a cousin&amp;rsquo;s wedding. For the first time, I could appreciate the gifts of a desert climate. There is much less visual input stimulating the brain. The plants don&amp;rsquo;t grow dense or tall so you can see everything around you. It&amp;rsquo;s calming not to wonder what&amp;rsquo;s behind the trees. And the heat makes you want to just sleep or sit in the pool or not do anything. If you need to rest or think, the dessert is a great place to do it.&lt;/p&gt;
&lt;p&gt;We drove to the &lt;a href=&#34;https://www.google.com/maps/place/Hi-View+Trailhead/@34.0760208,-116.4018785,17z/data=!3m1!4b1!4m6!3m5!1s0x80dad8be48859b39:0x8d3f1e64616c0b2f!8m2!3d34.0760208!4d-116.3993036!16s%2Fg%2F1q5bn9ppl?entry=ttu&#34;&gt;Hi-View Trailhead&lt;/a&gt; outside of Yucca Valley and walked about 100 feet down the path from the parking lot before turning back. Short, sweet and hot. But long enough to take some great photos of the Joshua Trees.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;./joshua-tree%20-%209.jpeg&#34;&gt;&lt;img src=&#34;./joshua-tree%20-%209.jpeg&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;Later that day we left the kids with Papa and headed to the wedding. Vanessa and Sarah put the final touches on the wedding cake which traveled all the way from San Francisco.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;./joshua-tree%20-%2010.jpeg&#34;&gt;&lt;img src=&#34;./joshua-tree%20-%2010.jpeg&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;The next day we stopped by Hemet, CA to visit grandparents. They fired up the 1940s era bulldozer that was in full working order.&lt;/p&gt;
&lt;figure&gt;&lt;a href=&#34;./joshua-tree%20-%2011.jpeg&#34;&gt;&lt;img src=&#34;./joshua-tree%20-%2011.jpeg&#34;/&gt;&lt;/a&gt;
&lt;/figure&gt;

&lt;p&gt;My favorite picture of the weekend. There is symbolism here that I can&amp;rsquo;t fully put words to. The great-grandmother with her cane, leaning the on the powerful machine of the past, looking down at the toddlers who carry the future. Just beautiful.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Jet Fuel</title>
      <link>https://www.jameskerr.blog/posts/jet-fuel/</link>
      <pubDate>Fri, 05 May 2023 16:10:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/jet-fuel/</guid>
      <description>&lt;p&gt;We build an engine for it.&lt;br&gt;
It propels us to new heights.&lt;br&gt;
It explodes.&lt;br&gt;
It destroys everything around it.&lt;br&gt;
It takes us to heavenly places.&lt;br&gt;
It burns.&lt;br&gt;
It brings us to life.&lt;br&gt;
It transcends the earth.&lt;br&gt;
Handle with care.&lt;br&gt;
Enjoy its power.&lt;br&gt;
Summon it for good.&lt;br&gt;
Rest in the potential within.&lt;br&gt;
Sacred energy.&lt;br&gt;
For purpose.&lt;br&gt;
For pleasure.&lt;br&gt;
For life.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>5 Tricks for Taking Great Photos</title>
      <link>https://www.jameskerr.blog/posts/tricks-for-taking-great-photos/</link>
      <pubDate>Thu, 04 May 2023 12:39:05 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/tricks-for-taking-great-photos/</guid>
      <description>&lt;p&gt;I’ve owned a full frame camera for over 5 years. I’ve acquired 3 lenses and snapped many thousands of photos. And yet, last week I discovered these five tricks and was shocked by my ignorance of them.&lt;/p&gt;
&lt;p&gt;While watching a lesson on incorporating images in website designs, my instructor presented these five qualities of great images.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Five Qualities of Great Images:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There should be one obvious subject in an image.&lt;/li&gt;
&lt;li&gt;The subject must be well lit and in focus.&lt;/li&gt;
&lt;li&gt;The center of the subject must be framed on a horizontal third or a vertical third or both.&lt;/li&gt;
&lt;li&gt;There needs to be a flow of energy in the photo for your eyes to follow, ideally towards the subject.&lt;/li&gt;
&lt;li&gt;The background must not be distracting.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let me break these down with my own experience.&lt;/p&gt;
&lt;h2 id=&#34;1-a-single-subject&#34;&gt;1. A Single Subject&lt;/h2&gt;
&lt;p&gt;I take pictures of landscapes and I often make the mistake of having no clear subject. Pick a cool tree or one of the buildings or the giant rock. It can be anything, but you must choose it. If you’re photographing a group of things, make a one or a few of them the clear protagonists of the image.&lt;/p&gt;
&lt;h2 id=&#34;2-well-lit-and-in-focus&#34;&gt;2. Well Lit and In Focus&lt;/h2&gt;
&lt;p&gt;This seems obvious, but I have taken otherwise great pictures where the people’s faces are a complete shadow. If I had only moved them a little into the light.&lt;/p&gt;
&lt;h2 id=&#34;3-rule-of-thirds&#34;&gt;3. Rule of Thirds&lt;/h2&gt;
&lt;p&gt;I learned part of this in film school, but now it’s hitting home. Divide the frame into thirds vertically and horizontally. Place your subject where those lines intersect. It looks pro.&lt;/p&gt;
&lt;h2 id=&#34;4-find-the-movement&#34;&gt;4. Find the Movement&lt;/h2&gt;
&lt;p&gt;This was brand new to me but so true! There needs to be a flow of energy through the photo. A path for eyes to follow. Ideally, the energy flows to the subject. For example, lines on the horizon, trunks of trees, the stem of a flower, or a long shadow. Lines. Paths. Flow. Motion. It’s a subtle thing. When photos have this “movement” in them, they just feel good, but most of us can’t say why. This is why!&lt;/p&gt;
&lt;h2 id=&#34;5-clean-background&#34;&gt;5. Clean Background&lt;/h2&gt;
&lt;p&gt;The subject should stand out from the background. Anything busy or random in the background is going to take away from that sweet shot.&lt;/p&gt;
&lt;p&gt;So next time you snap a pic:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick a subject&lt;/li&gt;
&lt;li&gt;Light it up&lt;/li&gt;
&lt;li&gt;Find the flow&lt;/li&gt;
&lt;li&gt;Simplify the background&lt;/li&gt;
&lt;li&gt;Frame it in thirds&lt;/li&gt;
&lt;li&gt;Snap&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These came from &lt;a href=&#34;https://www.erikdkennedy.com/&#34;&gt;Erik Kennedy&lt;/a&gt; in his online course &lt;a href=&#34;https://www.learnui.design/&#34;&gt;Learn UI Design&lt;/a&gt;. Thanks Erik!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>It’s a Gift for Me to Be Angry</title>
      <link>https://www.jameskerr.blog/posts/its-a-gift-for-me-to-be-angry/</link>
      <pubDate>Mon, 03 Apr 2023 14:43:39 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/its-a-gift-for-me-to-be-angry/</guid>
      <description>&lt;p&gt;When I’m angry I stop needing everyone to like me. I don’t care if they approve of what I do. My face doesn’t hurt from smiling all the time. When people talk to me, I give myself a moment to evaluate if I agree or not before automatically nodding in agreement.
It’s liberating.&lt;/p&gt;
&lt;p&gt;Some people on the other end of the spectrum are angry all the time. I’m on the overly agreeable side. I wish I could be 10% more angry to balance out my people pleasing defaults.&lt;/p&gt;
&lt;p&gt;I was just a retreat center near Petaluma, CA where a speaker spoke on balance. He said we all have the full spectrum of characteristics within us: anger, joy, selfishness, caring, pride, humility, etc. We get into trouble when one of them tries to be the only player on the field while all the others are benched. That’s when we make errors in our behavior. When this happens, we can pause and pray for the right counter-characteristics to arise within us to balance our response.&lt;/p&gt;
&lt;p&gt;Selfishness &amp;lt;&amp;gt; Caring&lt;/p&gt;
&lt;p&gt;Anger &amp;lt;&amp;gt; Peace&lt;/p&gt;
&lt;p&gt;Rigid &amp;lt;&amp;gt; Flexible&lt;/p&gt;
&lt;p&gt;Permissive &amp;lt;&amp;gt; Controlling&lt;/p&gt;
&lt;p&gt;Every single character trait is helpful in the right circumstance and destructive in the wrong circumstance.&lt;/p&gt;
&lt;p&gt;How cool. There’s nothing about me that I need to “remove”. It all belongs. I just need help learning the dosage required for the situation.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>13 Decisions to Make Before Publishing JavaScript to NPM</title>
      <link>https://www.jameskerr.blog/posts/13-decisions-to-make-before-publishing-to-npm/</link>
      <pubDate>Thu, 30 Mar 2023 09:00:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/13-decisions-to-make-before-publishing-to-npm/</guid>
      <description>&lt;p&gt;This post is half rant, half guide. Each of these 13 questions reveals tradeoffs that take time and mental energy to research. In case you don&amp;rsquo;t already have enough to decide today, here&amp;rsquo;s what you must consider when creating and publishing a new JavaScript package.&lt;/p&gt;
&lt;h3 id=&#34;1-do-you-want-to-write-it-in-typescript&#34;&gt;1. Do you want to write it in TypeScript?&lt;/h3&gt;
&lt;p&gt;If so, you&amp;rsquo;ll need to compile it before publishing. Bookmark the the &lt;a href=&#34;https://www.typescriptlang.org/tsconfig&#34;&gt;tsconfig.json&lt;/a&gt; reference.&lt;/p&gt;
&lt;h3 id=&#34;2-do-you-want-to-compile-using-tsc-swc-or-esbuild&#34;&gt;2. Do you want to compile using tsc, swc, or esbuild?&lt;/h3&gt;
&lt;p&gt;By default, TypeScript uses &lt;a href=&#34;https://www.typescriptlang.org/docs/handbook/compiler-options.html&#34;&gt;tsc&lt;/a&gt; to type check and transpile your source .ts files into regular JavaScript. It&amp;rsquo;s slower because it&amp;rsquo;s written in JavaScript. Other tools like &lt;a href=&#34;https://swc.rs/&#34;&gt;swc&lt;/a&gt; (Rust) or &lt;a href=&#34;https://esbuild.github.io/&#34;&gt;esbuild&lt;/a&gt; (Go) transpile much faster, but do not type check. Nobody likes waiting around for a build, but maybe you&amp;rsquo;d rather have one tool.&lt;/p&gt;
&lt;h3 id=&#34;3-do-you-want-it-to-run-in-node&#34;&gt;3. Do you want it to run in Node?&lt;/h3&gt;
&lt;p&gt;Now you need to decide your module format.&lt;/p&gt;
&lt;h3 id=&#34;4-do-you-want-to-output-your-package-as-esm-or-cjs&#34;&gt;4. Do you want to output your package as ESM or CJS?&lt;/h3&gt;
&lt;p&gt;&lt;a href=&#34;https://nodejs.org/api/modules.html#modules-commonjs-modules&#34;&gt;CommonJS modules&lt;/a&gt; (CJS) was the original way to include code from another file in Node. &lt;a href=&#34;https://nodejs.org/api/esm.html#modules-ecmascript-modules&#34;&gt;ECMAScript modules&lt;/a&gt; (ESM) is now the standard way to include JS code and Node just recently implemented support for it. The community wants us to move to &lt;a href=&#34;https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c&#34;&gt;pure ESM&lt;/a&gt;, but many things are CJS and they don&amp;rsquo;t exactly interop seamlessly.&lt;/p&gt;
&lt;h3 id=&#34;5-are-any-of-your-dependencies-pure-esm&#34;&gt;5. Are any of your dependencies pure ESM?&lt;/h3&gt;
&lt;p&gt;Using them in your CJS project is not going to work out of the box.&lt;/p&gt;
&lt;h3 id=&#34;6-are-any-of-your-dependents-cjs&#34;&gt;6. Are any of your dependents CJS?&lt;/h3&gt;
&lt;p&gt;They won&amp;rsquo;t be able to use your pure ESM library out of the box.&lt;/p&gt;
&lt;h3 id=&#34;7-since-node-can-run-esm-and-cjs-do-you-want-to-support-both&#34;&gt;7. Since Node can run ESM and CJS, do you want to support both?&lt;/h3&gt;
&lt;p&gt;Then you need to avoid the &lt;a href=&#34;https://nodejs.org/api/packages.html#dual-package-hazard&#34;&gt;Dual Package Hazard&lt;/a&gt; when both versions of your library might end up loaded at the same time.&lt;/p&gt;
&lt;h3 id=&#34;8-do-you-want-it-to-run-in-browsers&#34;&gt;8. Do you want it to run in browsers?&lt;/h3&gt;
&lt;p&gt;Since all browsers can run ESM, you&amp;rsquo;ll probably choose that as your module format, as opposed to &lt;a href=&#34;https://en.wikipedia.org/wiki/Asynchronous_module_definition&#34;&gt;AMD&lt;/a&gt; or an &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Glossary/IIFE&#34;&gt;IIFE&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;9-will-you-bundle-all-your-code-into-a-single-js-file&#34;&gt;9. Will you bundle all your code into a single JS file?&lt;/h3&gt;
&lt;p&gt;This will make it download faster. You probably want it minified too.&lt;/p&gt;
&lt;h3 id=&#34;10-will-you-inline-your-dependencies-in-the-bundle&#34;&gt;10. Will you inline your dependencies in the bundle?&lt;/h3&gt;
&lt;p&gt;This will make it download slower, but it will be more portable.&lt;/p&gt;
&lt;h3 id=&#34;11-will-you-use-a-cdn-to-get-it-into-the-browser&#34;&gt;11. Will you use a CDN to get it into the browser?&lt;/h3&gt;
&lt;p&gt;You&amp;rsquo;ll need to know about services like &lt;a href=&#34;https://www.jsdelivr.com/&#34;&gt;jsDelivr&lt;/a&gt; or &lt;a href=&#34;https://www.skypack.dev/&#34;&gt;Skypack&lt;/a&gt; which automatically serve packages in the npm registry. Some services let you leave the npm dependencies out of the bundle while their CDNs serve them for you. Otherwise your users will need to download the bundle like the old days or npm install it and link to it in node_modules from their HTML page. ES modules must have an explicit path to a real file, no index.js or package.json magic. Or you can use &lt;a href=&#34;https://github.com/WICG/import-maps&#34;&gt;import maps&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;12-do-you-want-to-bundle-it-for-node&#34;&gt;12. Do you want to bundle it for node?&lt;/h3&gt;
&lt;p&gt;Esbuild talks about doing this &lt;a href=&#34;https://esbuild.github.io/getting-started/#bundling-for-node&#34;&gt;in their docs&lt;/a&gt;. It can be faster to read from the disc.&lt;/p&gt;
&lt;h3 id=&#34;13-where-will-you-specify-your-entry-point-in-packagejson&#34;&gt;13. Where will you specify your entry point in package.json?&lt;/h3&gt;
&lt;p&gt;The top choices for your &lt;a href=&#34;https://nodejs.org/api/packages.html#package-entry-points&#34;&gt;entry point&lt;/a&gt; are: &lt;a href=&#34;https://nodejs.org/api/packages.html#main-entry-point-export&#34;&gt;&amp;ldquo;exports&amp;rdquo;&lt;/a&gt;, &lt;a href=&#34;https://nodejs.org/api/packages.html#main&#34;&gt;&amp;ldquo;main&amp;rdquo;&lt;/a&gt;, and &lt;a href=&#34;https://github.com/defunctzombie/package-browser-field-spec&#34;&gt;&amp;ldquo;browser&amp;rdquo;&lt;/a&gt;. Each runtime and build tool will use this information to run or compile your code.&lt;/p&gt;
&lt;h2 id=&#34;what-a-nightmare&#34;&gt;What. A. Nightmare.&lt;/h2&gt;
&lt;p&gt;To simply share some JavaScript you&amp;rsquo;ve got to consider JS runtimes, module formats, and build tools with no one true way. Was it easier when cross-browser compatibility was the big headache?&lt;/p&gt;
&lt;h2 id=&#34;my-answers&#34;&gt;My Answers&lt;/h2&gt;
&lt;p&gt;I had to spend the time making these decisions for my own package &lt;a href=&#34;https://www.npmjs.com/package/@brimdata/zed-js&#34;&gt;@brimdata/zed-js&lt;/a&gt;. A JavaScript client package for the &lt;a href=&#34;https://zed.brimdata.io/&#34;&gt;Zed&lt;/a&gt; data platform. Here&amp;rsquo;s what I came up with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will write TypeScript&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will use &lt;a href=&#34;https://nx.dev/&#34;&gt;nx&lt;/a&gt;&lt;/strong&gt; to generate the project because their developers have many sane decisions for me. Thank you!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will build a sole CJS version&lt;/strong&gt; for Node since I plan to consume this package in an Electron app and &lt;a href=&#34;https://github.com/electron/electron/issues/21457&#34;&gt;Electron doesn&amp;rsquo;t yet support ESM&lt;/a&gt;. This was a hard decision because &lt;a href=&#34;https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c?permalink_comment_id=3850849#gistcomment-3850849&#34;&gt;respected community members&lt;/a&gt; are pushing valiantly for pure ESM packages. I agree philosophically 100%. If I wasn&amp;rsquo;t the one consuming this package, I&amp;rsquo;d go pure ESM. However, I don&amp;rsquo;t feel like &lt;a href=&#34;https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c?permalink_comment_id=3850849#gistcomment-3850849&#34;&gt;hacking around Electron&lt;/a&gt; to get my own code to run, so I&amp;rsquo;m compromising on my values for today.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will use tsc to build&lt;/strong&gt; the CJS version to avoid another tool. If my project were huge, I&amp;rsquo;d use to swc to build and tsc to lint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will leave the CJS version unbundled&lt;/strong&gt; because&amp;hellip;&lt;a href=&#34;https://medium.com/s/silicon-satire/i-peeked-into-my-node-modules-directory-and-you-wont-believe-what-happened-next-b89f63d21558&#34;&gt;what happens in node_modules&lt;/a&gt;, stays in node_modules.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will use the &amp;ldquo;exports&amp;rdquo; field&lt;/strong&gt; in package.json to point to the Node entry point. It has some advantages over &amp;ldquo;main&amp;rdquo; and will make it easier to switch to pure ESM in the future.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will use esbuild&lt;/strong&gt; to bundle an ESM version for the web.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will inline my dependencies&lt;/strong&gt; because I don&amp;rsquo;t have many and they are not big. If they were big, I&amp;rsquo;d consider using a CDN like Skypack.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;I will use the &amp;ldquo;browser&amp;rdquo; field&lt;/strong&gt; in package.json to point to the bundle.&lt;/p&gt;
&lt;h2 id=&#34;hope-for-brighter-days&#34;&gt;Hope For Brighter Days&lt;/h2&gt;
&lt;p&gt;My introduction to programming came from Ruby on Rails. It championed the principle &amp;ldquo;convention over configuration&amp;rdquo; when the back-end landscape was overgrown with XML config files for Java VMs.&lt;/p&gt;
&lt;p&gt;Today we are in having a &amp;ldquo;configuration-heavy&amp;rdquo; moment in the front-end world, but I have hope that good conventions will emerge. Then we can get back to doing the real work.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Netflix Does Not Have My Best Interest</title>
      <link>https://www.jameskerr.blog/posts/netflix-does-not-have-my-best-interest/</link>
      <pubDate>Thu, 23 Mar 2023 18:59:18 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/netflix-does-not-have-my-best-interest/</guid>
      <description>&lt;p&gt;I watch Netflix because I’m too tired to do anything else. The kids are finally sleeping and I’m spent.&lt;/p&gt;
&lt;p&gt;It’s meant to be a quick escape from life before I do what I really need to do which is sleep.&lt;/p&gt;
&lt;p&gt;But then Netflix launches the attack!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They release every episode to a show all at once.&lt;/li&gt;
&lt;li&gt;They give me 3 seconds to stop the next show from automatically playing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m too tired to get up and stop the next episode! I’m watching because I’m too tired to do anything else. Theres not sufficient willpower to instantly stop the next show once the current one finishes resulting in me watching TV way later than I want to and becoming even more tired the next day. A vicious cycle.&lt;/p&gt;
&lt;p&gt;What if Netflix gave me 60 seconds to savor the last scene of that episode, see some credits, listen to some music, and decide if I want to make a good choice for my health or not.&lt;/p&gt;
&lt;p&gt;What if I could set my computer to force shut down after a given time frame. Set that up before the show starts. And it would need to really force close everything. If it gave me a popup to confirm I’d probably dismiss it. I can’t trust myself past 9pm.&lt;/p&gt;
&lt;p&gt;Netflix, your defaults are not looking out for me.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Embedded Divinity</title>
      <link>https://www.jameskerr.blog/posts/embedded-divinity/</link>
      <pubDate>Wed, 22 Mar 2023 10:52:26 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/embedded-divinity/</guid>
      <description>&lt;p&gt;What if we didn&amp;rsquo;t think of God as infinite.&lt;/p&gt;
&lt;p&gt;What if God poured itself into this &lt;em&gt;particular&lt;/em&gt; universe. What if He&amp;rsquo;s completely bound to this one cosmos. It&amp;rsquo;s life is &lt;em&gt;it&amp;rsquo;s&lt;/em&gt; life. Our &amp;ldquo;being&amp;rdquo; is only because the Source is fully giving itself to the existence.&lt;/p&gt;
&lt;p&gt;If we think this way, God is embedded into the material around us. He&amp;rsquo;s not a distant cosmic scientist creating life in his lab, doing experiments, always able to try again, no one instance too important.&lt;/p&gt;
&lt;p&gt;What if this is the only finite instance! God has gone all in, nothing saved for a backup plan, fully committed.&lt;/p&gt;
&lt;p&gt;Thinking like this helps me want to love God and my existence. Maybe God risked everything for this one universe. There is no Plan B. If things go wrong, there is no do-over; only repair, forgiveness, recovery, and mercy.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Adults Don&#39;t Share</title>
      <link>https://www.jameskerr.blog/posts/adults-dont-share/</link>
      <pubDate>Fri, 23 Dec 2022 09:00:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/adults-dont-share/</guid>
      <description>&lt;p&gt;All the time at the playground I hear, &amp;ldquo;good sharing!&amp;rdquo; and &amp;ldquo;no no Jackson, you need to share&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;We parents think it is some critical skill that children need to learn. A skill that must be forcefully developed in order to be a contributing member of society.&lt;/p&gt;
&lt;p&gt;Then one day I thought: We adults never share! How weird would it be to ask for someone&amp;rsquo;s phone to play a game.&lt;/p&gt;
&lt;p&gt;- &amp;ldquo;Do you have any apps on your phone I could play with for a while&amp;rdquo;&lt;/p&gt;
&lt;p&gt;- &amp;ldquo;Yeah, I&amp;rsquo;d love to share my phone with you. Have fun! I&amp;rsquo;ll set a timer so we know when it&amp;rsquo;s my turn again.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;- &amp;ldquo;While you wait, here are the keys to my car. It&amp;rsquo;s the Audi A5 over there.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;What good sharing!&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s probably not harming kids to help them understand what sharing is, but I certainly don&amp;rsquo;t practice what I preach. I wonder why we care so much about sharing at the toddler stages.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>TypeScript Classes Are Giving Me Carpal Tunnel</title>
      <link>https://www.jameskerr.blog/posts/typescript-classes-verbose/</link>
      <pubDate>Wed, 21 Dec 2022 10:37:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/typescript-classes-verbose/</guid>
      <description>&lt;p&gt;Whenever a class needs a few arguments in TypeScript, I cringe because I know I&amp;rsquo;m going to need to perform a ceremony to make it happy.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with the simple case. If a Class needs two arguments, I&amp;rsquo;d do this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Point&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is some repetition to get the types and initialization right. Fortunately, the TypeScript authors created a shorthand syntax to calm this down.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Point&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;number&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Very nice. By providing a typescript keyword &lt;code&gt;public&lt;/code&gt;, &lt;code&gt;private&lt;/code&gt;, &lt;code&gt;protected&lt;/code&gt;, or &lt;code&gt;readonly&lt;/code&gt; before the argument name, the class will make it a member variable and initialize it with the argument for you!&lt;/p&gt;
&lt;p&gt;This is amazing but&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GiantObjectWithManyArgs&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;init&amp;#34;&lt;/span&gt;, () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;callback&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;hellip;it breaks down when you have more than 1 or 2 arguments. To fix this, a common pattern is to pass a single object argument.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GiantObjectWithManyArgs&lt;/span&gt;({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;myField&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;myTable&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;init&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;onComplete&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;callback&amp;#34;&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is so much better than positional arguments.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They have names!&lt;/li&gt;
&lt;li&gt;The order doesn&amp;rsquo;t matter!&lt;/li&gt;
&lt;li&gt;The optional args can be omitted!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In JavaScript, this would be a piece of cake to setup in the constructor.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GiantObjectWithManyArgs&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Object.&lt;span style=&#34;color:#a6e22e&#34;&gt;assign&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Everything in the args object becomes a member of the class. But in TypeScript, the amount of code needed to make this work explodes!&lt;/p&gt;
&lt;p&gt;This is the least verbose way I know to write it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Args&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;zed.Field&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;zed.Table&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;GiantObject&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;GiantObject&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;init&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;complete&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;onComplete&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?:&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GiantObject&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;field&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;table&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;parent&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;children&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;status&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;onComplete&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;[&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;onComplete&amp;#34;&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;onComplete&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;onComplete&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve got blisters on my fingers!&lt;/p&gt;
&lt;p&gt;TypeScript authors&amp;hellip;you saw the the need for positional argument shorthand. I&amp;rsquo;m sure you see the need for object argument shorthand as well.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m no language designer so here is my blind stab in the dark for syntax ideas.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Args&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;zed.Field&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;table&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;zed.Table&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;parent&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;GiantObject&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;children&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;GiantObject&lt;/span&gt;[] &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;status&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;init&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;|&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;complete&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;onComplete&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;?:&lt;/span&gt; () &lt;span style=&#34;color:#f92672&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Not Real TypeScript
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GiantObject&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;assign&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// Aww, it&amp;#39;s probably hard to specifiy
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// some as a private and others as public...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Or mabye
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GiantObjects&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;assigns&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Args&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;] &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;workaround&#34;&gt;Workaround&lt;/h2&gt;
&lt;p&gt;My workaround for this is to assign the whole object to a member variable called &amp;ldquo;args&amp;rdquo;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Args&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GiantObject&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;constructor&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;args&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;Args&lt;/span&gt;) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then I have to make getters for each of the pieces I need. It&amp;rsquo;s not a bad way to go, but I&amp;rsquo;m coding around something because the tooling makes the preferred style difficult.&lt;/p&gt;
&lt;p&gt;Object arguments are so much better than positional arguments, but the boilerplate in TypeScript compared to plain JavaScript makes them almost not worth it.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Sharing Steps in Github Action Workflows</title>
      <link>https://www.jameskerr.blog/posts/sharing-steps-in-github-action-workflows/</link>
      <pubDate>Mon, 13 Jun 2022 13:00:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/sharing-steps-in-github-action-workflows/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve got an &lt;a href=&#34;https://github.com/brimdata/brim&#34;&gt;electron app&lt;/a&gt; that needs to be built on all three platforms. I&amp;rsquo;ve got three workflow files all with slightly differing steps. Is there way to share whats common? Yes. It&amp;rsquo;s called &lt;a href=&#34;https://docs.github.com/en/actions/creating-actions/about-custom-actions#composite-actions&#34;&gt;composite actions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (Jan 4, 2024)&lt;/strong&gt;: There is another way to reuse workflows thats described in the &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/reusing-workflows#creating-a-reusable-workflow&#34;&gt;reusing workflows&lt;/a&gt; section of the docs.&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-composite-action&#34;&gt;What is a Composite Action?&lt;/h2&gt;
&lt;p&gt;The name is not the most intuitive (why not &amp;ldquo;shared action&amp;rdquo;?), but it&amp;rsquo;s a group of steps that are intended to be &amp;ldquo;used&amp;rdquo; within a workflow file. This is GitHub&amp;rsquo;s solution for sharing steps between workflows. Here is an example of the simplest composite action.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;My Shared Steps&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;runs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;using&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;composite&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;-- this is the important part&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;echo &amp;#34;Hello, World&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, make a file like this and set the top-level &lt;code&gt;runs:&lt;/code&gt; property to the string &lt;code&gt;&amp;quot;composite&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Also, &lt;a href=&#34;https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#runs-for-composite-actions&#34;&gt;Action&lt;/a&gt; files are different from &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun&#34;&gt;Workflow&lt;/a&gt; files. Once such difference is that your steps must specify a &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell&#34;&gt;shell&lt;/a&gt; property if they use &lt;code&gt;run:&lt;/code&gt; . That knowledge will save you from this error.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.jameskerr.blog/img/sharing-steps-in-github-action-workflows/shell.png&#34; alt=&#34;Shell Error&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;where-do-i-put-this-file&#34;&gt;Where do I put this file?&lt;/h2&gt;
&lt;p&gt;It can go anywhere in your repo. Or in it&amp;rsquo;s own repo. But, I want mine in the &lt;code&gt;.github/actions&lt;/code&gt; folder at the root of my repository. Something like:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;.github/actions/my-shared-steps/action.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notice that I needed to create a directory containing a file called &lt;code&gt;action.yml&lt;/code&gt;. This is the required structure for an action. I tried naming the file something else and it didn&amp;rsquo;t like that.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://www.jameskerr.blog/img/sharing-steps-in-github-action-workflows/action.png&#34; alt=&#34;Action Error&#34;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Important: the action must be a directory containing an action.yml file.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;how-do-i-use-it&#34;&gt;How do I use it?&lt;/h2&gt;
&lt;p&gt;If you read that error above closely, you noticed an important catch. Before I could use this action, I had to first checkout the repo. That makes sense, since the file is&amp;hellip;well&amp;hellip;in the repo. Here&amp;rsquo;s how the steps of my workflow job look:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	- &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@v3&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# required first&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	- &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;./.github/actions/my-shared-steps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since I am using an &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-using-an-action-in-the-same-repository-as-the-workflow&#34;&gt;action that is in our repo&lt;/a&gt;, I specify the path to the file from the root of the repository starting with a &lt;code&gt;./&lt;/code&gt; (that&amp;rsquo;s required). If your action is not in the repo, &lt;a href=&#34;https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses&#34;&gt;here are your options&lt;/a&gt; for using it.&lt;/p&gt;
&lt;h2 id=&#34;passing-data-to-the-action&#34;&gt;Passing Data to the Action&lt;/h2&gt;
&lt;p&gt;More errors revealed that I couldn&amp;rsquo;t use the &lt;code&gt;${{ secrets }}&lt;/code&gt; variable in my composite action. I had to pass it data using the &lt;code&gt;inputs:&lt;/code&gt; property in the action, and the &lt;code&gt;with:&lt;/code&gt; property in the workflow. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# .github/actions/build-win/action.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Build Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;description&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Specific steps to build for Windows&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;inputs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;csc_key_password&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;required&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;csc_link&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;required&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;gh_token&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;required&lt;/span&gt;: &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;runs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;using&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    - &lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Build Signed Release&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;shell&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;bash&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;run&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;yarn electron-builder --win&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;env&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;CSC_KEY_PASSWORD&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.csc_key_password }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;CSC_LINK&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.csc_link }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;GH_TOKEN&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ inputs.gh_token }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And I used it like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;# .github/workflows/win-release-candidate.yml&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;name&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;Brim Windows release candidate creation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;on&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;pull_request&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;runs-on&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;windows-2019&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;actions/checkout@v2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;./.github/actions/setup-brim&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#f92672&#34;&gt;uses&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;./.github/actions/build-win&lt;/span&gt; &lt;span style=&#34;color:#75715e&#34;&gt;# &amp;lt;--- Using the composite action here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;with&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;csc_key_password&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.WINDOWS_SIGNING_PASSPHRASE }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;csc_link&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.WINDOWS_SIGNING_PFX_BASE64 }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#f92672&#34;&gt;gh_token&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;can-i-publish-it&#34;&gt;Can I publish it?&lt;/h2&gt;
&lt;p&gt;Yes. This article in the docs titled &amp;ldquo;&lt;a href=&#34;https://docs.github.com/en/actions/creating-actions/creating-a-composite-action&#34;&gt;Creating a composite action&lt;/a&gt;&amp;rdquo; shows how to make a compsite action in it&amp;rsquo;s own repo with inputs and outputs, then uses it in a workflow.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve now DRY&amp;rsquo;d out my CI workflows with composite actions. One installs Go and Node then runs yarn install. Another publishes artifacts to our private Google Cloud bucket. It&amp;rsquo;s great.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Post Script&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Debugging CI is a pain. You know. You edit a workflow file, you commit, you push, you open the browser, you wait, it fails, your YAML was not indented enough, repeat. It sucks. There has got to be a better way. To start, it would be great to at least validate my workflow file with some CLI tool before I push. Or a local VM I could test it in? If this exists already, someone please tell me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (Jan 4, 2024)&lt;/strong&gt;: I have been informed there is a tool for this called &lt;a href=&#34;https://github.com/nektos/act&#34;&gt;act&lt;/a&gt;. I have not used it yet, but the repo has 46K stars so they must be doing something right.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>How to useRef and forwardRef in React</title>
      <link>https://www.jameskerr.blog/posts/react-useref-and-forward-ref/</link>
      <pubDate>Tue, 03 May 2022 00:00:00 -0700</pubDate>
      
      <guid>https://www.jameskerr.blog/posts/react-useref-and-forward-ref/</guid>
      <description>&lt;p&gt;What do I do when my component uses a ref internally but also needs to forward a ref from its parent? The short answer is merge the refs into a single ref function. If what that means is not immediatly obvious, you&amp;rsquo;re like me. Read on.&lt;/p&gt;
&lt;h2 id=&#34;what-is-a-ref&#34;&gt;What is a ref?&lt;/h2&gt;
&lt;p&gt;Look at what get&amp;rsquo;s returned from &lt;em&gt;useRef&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* prints */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This ref is an object with one property &lt;em&gt;current&lt;/em&gt; that can be set to whatever you want.&lt;/p&gt;
&lt;p&gt;What happens when I pass it to an HTML element?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For years I just thought this was magic, but today I grepped &amp;ldquo;ref&amp;rdquo; in my &lt;code&gt;node_modules/react-dom&lt;/code&gt; folder and it&amp;rsquo;s not complicated. After react-dom creates the HTML button instance, it will attach the ref like so:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;function&amp;#39;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  	&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;instanceToUse&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  	&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;instanceToUse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;em&gt;ref&lt;/em&gt; prop will only accept three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;a function&lt;/li&gt;
&lt;li&gt;a plain object with a single &lt;em&gt;current&lt;/em&gt; property&lt;/li&gt;
&lt;li&gt;null&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means these two lines result in the same thing.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{(&lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;what-about-forwardref&#34;&gt;What about forwardRef?&lt;/h2&gt;
&lt;p&gt;When in doubt, console.log it out.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyButton&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardRef&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  ...
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What gets printed depends on the ref value given to the component. If you create that &lt;em&gt;MyButton&lt;/em&gt; component without passing it a ref, you&amp;rsquo;ll see &lt;code&gt;null&lt;/code&gt; printed to the console.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;MyButton&lt;/span&gt; /&amp;gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// with no ref
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* prints */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s try it with a ref from &lt;em&gt;useRef&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;MyButton&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* prints */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;undefined&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ok, this is starting to make sense. Whatever you pass as the &lt;em&gt;ref&lt;/em&gt; prop to the &lt;em&gt;MyButton&lt;/em&gt; component will be the same as second argument in the &lt;em&gt;forwardRef&lt;/em&gt; function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;MyButton&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;} /&amp;gt;; &lt;span style=&#34;color:#75715e&#34;&gt;// this ref is the same as...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;forwardRef&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;) {}); &lt;span style=&#34;color:#75715e&#34;&gt;// ...this ref
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The name &amp;ldquo;&lt;em&gt;forwardRef&lt;/em&gt;&amp;rdquo; is starting to make a lot of sense. Let&amp;rsquo;s test this out by passing a function as the &lt;em&gt;ref&lt;/em&gt; property.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;node&lt;/span&gt;) =&amp;gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;console&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;log&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Hi&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;MyButton&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* prints */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;(node) =&amp;gt; console.log(&amp;#34;Hi&amp;#34;)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;/* the string representation of the function */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;merging-two-refs&#34;&gt;Merging Two Refs&lt;/h2&gt;
&lt;p&gt;So to deliver on the promise of this blog&amp;rsquo;s title, here&amp;rsquo;s how we can have &lt;em&gt;useRef&lt;/em&gt; and &lt;em&gt;forwardRef&lt;/em&gt; in the same function component.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyButton&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardRef&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardedRef&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;localRef&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{(&lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// first we set the local ref
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;localRef&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// then we handle the forwarded ref
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#75715e&#34;&gt;// it can be a function, an object, or null
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardedRef&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;function&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardedRef&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardedRef&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;object&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardedRef&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  );
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This uses a function to properly set both the local and the forwarded refs.&lt;/p&gt;
&lt;p&gt;At this point, you probably can&amp;rsquo;t help yourself from writing this &lt;em&gt;mergeRefs&lt;/em&gt; function.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mergeRefs&lt;/span&gt;(...&lt;span style=&#34;color:#a6e22e&#34;&gt;refs&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;refs&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;forEach&lt;/span&gt;((&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;) =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;typeof&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;function&amp;#34;&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      } &lt;span style=&#34;color:#66d9ef&#34;&gt;else&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;current&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;instance&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To be used like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-jsx&#34; data-lang=&#34;jsx&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyButton&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardRef&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;props&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardedRef&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;localRef&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;useRef&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ref&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;{&lt;span style=&#34;color:#a6e22e&#34;&gt;mergeRefs&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;localRef&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;forwardedRef&lt;/span&gt;)} /&amp;gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Turns out, &lt;a href=&#34;https://twitter.com/neoziro&#34;&gt;Greg Bergé&lt;/a&gt; has created a utility that does just this called &lt;a href=&#34;https://github.com/gregberge/react-merge-refs&#34;&gt;react-merge-refs&lt;/a&gt;. That &lt;em&gt;mergeRefs&lt;/em&gt; function above is almost copied strait from his code. Thanks Greg.&lt;/p&gt;
&lt;p&gt;So that&amp;rsquo;s how you do it.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>