<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Robert's Blog]]></title><description><![CDATA[Robert's Blog]]></description><link>https://blog.robertbroersma.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 14:12:18 GMT</lastBuildDate><atom:link href="https://blog.robertbroersma.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Accessible Tabs Using React & Tailwind CSS]]></title><description><![CDATA[We're going for tabs with automatic activation. This means a tab panel will be revealed as soon as it's respective tab receives focus either by click or by using the arrow keys.
We want to re-use the tabs functionality in many places, with many diffe...]]></description><link>https://blog.robertbroersma.com/accessible-tabs-using-react-tailwind-css</link><guid isPermaLink="true">https://blog.robertbroersma.com/accessible-tabs-using-react-tailwind-css</guid><category><![CDATA[React]]></category><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[Accessibility]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Tue, 20 Oct 2020 12:39:03 GMT</pubDate><content:encoded><![CDATA[<p>We're going for tabs with automatic activation. This means a tab panel will be revealed as soon as it's respective tab receives focus either by <strong>click</strong> or by using the <strong>arrow keys</strong>.</p>
<p>We want to re-use the tabs functionality in many places, with many different styles. So the tab component must:</p>
<ul>
<li>Be re-usable.</li>
<li>Be completely unstyled.</li>
<li>Integrate well with Tailwind CSS.</li>
</ul>
<p>Requirements for accessibility <a target="_blank" href="https://www.w3.org/TR/wai-aria-practices/#tabpanel">From W3</a>:</p>
<ul>
<li><code>Tab</code>: When focus moves into the tab list, places focus on the active tab element.</li>
<li>When focus is on a tab element in a horizontal tab list:<ul>
<li><code>Left Arrow</code>: moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab.</li>
<li><code>Right Arrow</code>: Moves focus to the next tab. If focus is on the last tab element, moves focus to the first tab.</li>
</ul>
</li>
<li>When focus is on a tab element in a vertical tab list:<ul>
<li><code>Up Arrow</code>: moves focus to the previous tab. If focus is on the first tab, moves focus to the last tab.</li>
<li><code>Down Arrow</code>: Moves focus to the next tab. If focus is on the last tab element, moves focus to the first tab.</li>
</ul>
</li>
<li>When focus is on a tab in a tablist with either horizontal or vertical orientation:<ul>
<li><code>Space</code> or <code>Enter</code>: Activates the tab.</li>
<li><code>Home</code>: Moves focus to the first tab.</li>
<li><code>End</code>: Moves focus to the last tab.</li>
</ul>
</li>
</ul>
<h2 id="heading-funk">Funk</h2>
<p>I've created a package called Funk, which contains a set of components to create Tabs.</p>
<pre><code><span class="hljs-attribute">yarn</span> add <span class="hljs-variable">@statikly</span>/funk
</code></pre><p>or</p>
<pre><code>npm install <span class="hljs-operator">-</span><span class="hljs-operator">-</span>save @statikly<span class="hljs-operator">/</span>funk
</code></pre><p>You can then use it in such fashion:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { TabGroup } <span class="hljs-keyword">from</span> <span class="hljs-string">'@statikly/funk'</span>

<span class="hljs-keyword">const</span> Page = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"h-screen w-screen flex flex-col justify-center items-center"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup</span> <span class="hljs-attr">numTabs</span>=<span class="hljs-string">{3}</span> <span class="hljs-attr">direction</span>=<span class="hljs-string">{TabGroup.direction.HORIZONTAL}</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup.TabList</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup.Tab</span>
            <span class="hljs-attr">index</span>=<span class="hljs-string">{0}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"h-12 px-12 transition-colors duration-150"</span>
            <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"bg-black text-white"</span>
            <span class="hljs-attr">inactiveClassName</span>=<span class="hljs-string">"text-black"</span>
          &gt;</span>
            Tab 1
          <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup.Tab</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup.Tab</span>
            <span class="hljs-attr">index</span>=<span class="hljs-string">{1}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"h-12 px-12 transition-colors duration-150"</span>
            <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"bg-black text-white"</span>
            <span class="hljs-attr">inactiveClassName</span>=<span class="hljs-string">"text-black"</span>
          &gt;</span>
            Tab with input
          <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup.Tab</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup.Tab</span>
            <span class="hljs-attr">index</span>=<span class="hljs-string">{2}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"h-12 px-12 transition-colors duration-150"</span>
            <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"bg-black text-white"</span>
            <span class="hljs-attr">inactiveClassName</span>=<span class="hljs-string">"text-black"</span>
          &gt;</span>
            Tab 3
          <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup.Tab</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup.TabList</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup.TabPanel</span>
          <span class="hljs-attr">index</span>=<span class="hljs-string">{0}</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"p-16 transition-all transform h-64"</span>
          <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"opacity-100 duration-500 translate-x-0"</span>
          <span class="hljs-attr">inactiveClassName</span>=<span class="hljs-string">"absolute opacity-0 -translate-x-2"</span>
        &gt;</span>
          Content 1
        <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup.TabPanel</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup.TabPanel</span>
          <span class="hljs-attr">index</span>=<span class="hljs-string">{1}</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"p-16 transition-all transform h-64 flex flex-col"</span>
          <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"opacity-100 duration-500 translate-x-0"</span>
          <span class="hljs-attr">inactiveClassName</span>=<span class="hljs-string">"absolute opacity-0 -translate-x-2"</span>
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-1"</span> <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"input"</span>&gt;</span>
            Input
          <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">"input"</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-gray-400 px-8 h-12"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Focus me!"</span>
          /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup.TabPanel</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">TabGroup.TabPanel</span>
          <span class="hljs-attr">index</span>=<span class="hljs-string">{2}</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"p-16 transition-all transform h-64"</span>
          <span class="hljs-attr">activeClassName</span>=<span class="hljs-string">"opacity-100 duration-500 translate-x-0"</span>
          <span class="hljs-attr">inactiveClassName</span>=<span class="hljs-string">"absolute opacity-0 -translate-x-2"</span>
        &gt;</span>
          Content 3
        <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup.TabPanel</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">TabGroup</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-12 h-12 px-12 bg-indigo-500 text-white"</span>&gt;</span>
        Outside button
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>Check out the Codesandbox and give it a whirl!</p>
<p>{% codesandbox funk-example-uw048 %}</p>
<p>Prefer to get the code yourself or take a look under the hood? <a target="_blank" href="https://github.com/RobertBroersma/funk">Check out the repo here!</a></p>
]]></content:encoded></item><item><title><![CDATA[Big Heads - Easily generate characters for your projects]]></title><description><![CDATA[Big Heads
Hi all!
Today I launched my random character generator called Big Heads.
You can combine expressions, clothing, hair styles and colors into billions of different unique characters. Embed them on your website, use them in your favourite desi...]]></description><link>https://blog.robertbroersma.com/big-heads-easily-generate-characters-for-your-projects</link><guid isPermaLink="true">https://blog.robertbroersma.com/big-heads-easily-generate-characters-for-your-projects</guid><category><![CDATA[React]]></category><category><![CDATA[SVG]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Thu, 09 Jul 2020 07:02:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866540470/XJrJ5QQ50.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://bigheads.io/">Big Heads</a></p>
<p>Hi all!</p>
<p>Today I launched my random character generator called Big Heads.</p>
<p>You can combine expressions, clothing, hair styles and colors into billions of different unique characters. Embed them on your website, use them in your favourite design software, or import them from the React library.</p>
<p>All for free!</p>
<p>Heavily inspired by <a target="_blank" href="https://twitter.com/pablostanley">Pablo Stanley</a>'s <a target="_blank" href="https://avataaars.com/">Avataaars</a></p>
<p><a target="_blank" href="https://bigheads.io/">Go check it out!</a></p>
<p>Here's me as a Big Head!
<img src="https://bit.ly/3iDh7Fz" alt="A generated character depicting the author" /></p>
<p>Share your avatar in the comments! (You'll have to use a url shorten-er, because dev.to escapes the query params in the url). </p>
]]></content:encoded></item><item><title><![CDATA[Authorization in GraphQL]]></title><description><![CDATA[Authentication is figuring out who the user is.
Authorization is figuring out what the user is allowed to do.
For this article I'll be focusing on the latter.
Authenticate
Before we can focus on authorization, we do need to be authenticated. So witho...]]></description><link>https://blog.robertbroersma.com/authorization-in-graphql</link><guid isPermaLink="true">https://blog.robertbroersma.com/authorization-in-graphql</guid><category><![CDATA[GraphQL]]></category><category><![CDATA[Apollo GraphQL]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Sun, 05 Apr 2020 10:11:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866740876/6iWesB0B0.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Authentication</strong> is figuring out who the user is.
<strong>Authorization</strong> is figuring out what the user is allowed to do.</p>
<p>For this article I'll be focusing on the latter.</p>
<h1 id="heading-authenticate">Authenticate</h1>
<p>Before we <em>can</em> focus on authorization, we do need to be authenticated. So without diving in too deep, this is how we'll let our resolvers (and other parts of our app, a you will see) know who we are.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">new</span> ApolloServer({
  typeDefs,
  resolvers,
  <span class="hljs-attr">context</span>: <span class="hljs-keyword">async</span> ({ req }) =&gt; {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> getUser(req.header.authorization)

    <span class="hljs-keyword">return</span> { user }
  },
})
</code></pre>
<h1 id="heading-basic-authorization">Basic Authorization</h1>
<p>Now that we know if the user is authenticated or not, the most basic thing we can do is allow or deny them to do anything.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">new</span> ApolloServer({
  typeDefs,
  resolvers,
  <span class="hljs-attr">context</span>: <span class="hljs-keyword">async</span> ({ req }) =&gt; {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> getUser(req.header.authorization)

    <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> AuthorizationError(<span class="hljs-string">'You are not logged-in!'</span>);    

    <span class="hljs-keyword">return</span> { user }
  },
})
</code></pre>
<p>The context function always runs before any requested query. IF there's no user found that matches the <code>authorization</code> header, the entire request is blocked!</p>
<h1 id="heading-group-authorization">Group Authorization</h1>
<p>The next thing we want to do is allow the user access to <em>parts</em> of the schema if they are allowed. Let's say we only allow the doctors to access patient info. It would now be very tempting to go into the resolver for the <code>patients</code> query and do this:</p>
<pre><code class="lang-javascript">patients: <span class="hljs-function">(<span class="hljs-params">root, args, context</span>) =&gt;</span> {
  <span class="hljs-comment">// We could also throw an error again. That's up to you!</span>
  <span class="hljs-keyword">if</span> (context.user.role !== <span class="hljs-string">'doctor'</span>) <span class="hljs-keyword">return</span> [];

  <span class="hljs-keyword">return</span> [<span class="hljs-string">'Carl'</span>, <span class="hljs-string">'Hank'</span>];
}
</code></pre>
<p>However, this could quickly become a problem when there's multiple to ways to query for patient data. If our full schema looked like this:</p>
<pre><code class="lang-graphql">type Patient {
  name: String
}

type Hospital {
  name: String
  patients: [Patient!]!
}

type Query {
  patients: [Patient!]!
  hospitals: [Hospital!]!
}
</code></pre>
<p>Now we need to duplicate the <code>doctor</code> check inside the <code>hospitals</code> resolver!</p>
<h2 id="heading-models">Models</h2>
<p>A better way to do this is by splitting your authorization logic into a separate layer, separated from your resolver logic. This is described in more detail in <a target="_blank" href="https://graphql.org/learn/thinking-in-graphs/">Thinking in Graphs</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866740876/6iWesB0B0.png" alt="Business Layer" /></p>
<p>We can for instance create a model for the <code>Patient</code> type that looks like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Patient = {
  getAll() {...}
  getById(id) {...}
  getByHospitalId(hospitalId) {...}
}
</code></pre>
<p>Now our <code>Patient</code> model is the single source of truth for business logic concerning our Patient type! We just need a way to tell our models about our authenticated user.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// models/Patient.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> createPatientModel = <span class="hljs-function">(<span class="hljs-params">{ user }</span>) =&gt;</span> ({
  getAll() {...}
  getById(id) {...}
  getByHospitalId(hospitalId) {...}
})

<span class="hljs-comment">// graphl handler</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> handler = <span class="hljs-keyword">new</span> ApolloServer({
  typeDefs,
  resolvers,
  <span class="hljs-attr">context</span>: <span class="hljs-keyword">async</span> ({ req }) =&gt; {
    <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> getUser(req.header.authorization)

    <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> AuthorizationError(<span class="hljs-string">'You are not logged-in!'</span>);

    <span class="hljs-keyword">const</span> Patient = createPatientModel({ user })

    <span class="hljs-keyword">return</span> { user, <span class="hljs-attr">models</span>: { Patient } }
  },
})
</code></pre>
<p>There we go! We pass the Patient model and any other models on the context as well, so they're accessible in our resolvers:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Query.patients resolver</span>
<span class="hljs-attr">patients</span>: <span class="hljs-function">(<span class="hljs-params">root, args, context</span>) =&gt;</span> {

  <span class="hljs-comment">// The doctor authorization is handled in the Patient model</span>
  <span class="hljs-keyword">return</span> context.models.Patient.getAll()
}
</code></pre>
<h1 id="heading-ownership-authorization">Ownership Authorization</h1>
<p>Let's say we want even more fine-grained authorization for our doctors. Doctors should only be able to view information about their own patients. We can now go into our Patient model and change the required permissions, as opposed to editing all the different resolvers. Here's an example of filtering a doctor's patients if you're using <code>Prisma 2</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> createPatientModel = <span class="hljs-function">(<span class="hljs-params">{ user }</span>) =&gt;</span> ({
  getAll() {
    <span class="hljs-keyword">return</span> db.patient.findMany({ <span class="hljs-attr">where</span>: { <span class="hljs-attr">doctor</span>: { <span class="hljs-attr">id</span>: user.id } } })
  },
  getByHospitalId(hospitalId) {
    <span class="hljs-keyword">return</span> db.patient.findMany({
      <span class="hljs-attr">where</span>: { <span class="hljs-attr">doctor</span>: { <span class="hljs-attr">id</span>: user.id }, <span class="hljs-attr">hospital</span>: { <span class="hljs-attr">id</span>: hospitalId } }
    })
  }
})
</code></pre>
<p>I'll leave it up to you to generalize the ownership part of the <code>where</code> clause!</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>There's many different ways to handle authorization in a GraphQL server. This is my favourite one. Separating your business/authorization logic from your resolver logic makes your code more readable and maintainable!</p>
]]></content:encoded></item><item><title><![CDATA[Undo/Redo in React Using XState]]></title><description><![CDATA[I recently came across the need for undo and redo functionality in my app.
The app is an editor of sorts that allows you to add stuff and remove stuff using several different tools and keyboard shortcuts. All implemented using xstate.
It would be gre...]]></description><link>https://blog.robertbroersma.com/undoredo-in-react-using-xstate</link><guid isPermaLink="true">https://blog.robertbroersma.com/undoredo-in-react-using-xstate</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[xstate]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Sun, 22 Mar 2020 14:11:23 GMT</pubDate><content:encoded><![CDATA[<p>I recently came across the need for undo and redo functionality in my app.</p>
<p>The app is an editor of sorts that allows you to add stuff and remove stuff using several different tools and keyboard shortcuts. All implemented using <code>xstate</code>.</p>
<p>It would be great to be able to undo and redo actions taken in the editor! Let's see how we can implement <a target="_blank" href="https://redux.js.org/recipes/implementing-undo-history/">the undo/redo pattern from the Redux docs</a> in XState.</p>
<p>Let's say we have the following machine:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> editorMachine = Machine(
  {
    <span class="hljs-attr">id</span>: <span class="hljs-string">"editor"</span>,
    <span class="hljs-attr">context</span>: {
      <span class="hljs-attr">items</span>: []
    },
    <span class="hljs-attr">initial</span>: <span class="hljs-string">"normal"</span>,
    <span class="hljs-attr">on</span>: {
      <span class="hljs-attr">DELETE_SHAPE</span>: {
        <span class="hljs-attr">actions</span>: [<span class="hljs-string">"deleteShape"</span>]
      }
    },
    <span class="hljs-attr">states</span>: {
      <span class="hljs-attr">normal</span>: {
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">TOGGLE_MODE</span>: <span class="hljs-string">"turbo"</span>,
          <span class="hljs-attr">ADD_SHAPE</span>: {
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">"addShape"</span>]
          }
        }
      },
      <span class="hljs-attr">turbo</span>: {
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">TOGGLE_MODE</span>: <span class="hljs-string">"normal"</span>,
          <span class="hljs-attr">ADD_SHAPE</span>: {
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">"addThreeShapes"</span>]
          }
        }
      }
    }
  },
  {
    <span class="hljs-attr">actions</span>: {
      <span class="hljs-attr">addShape</span>: assign({
        <span class="hljs-attr">items</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> [...ctx.items, e.shape]
      }),
      <span class="hljs-attr">addThreeShapes</span>: assign({
        <span class="hljs-attr">items</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> [...ctx.items, e.shape, e.shape, e.shape]
      }),
      <span class="hljs-attr">deleteShape</span>: assign({
        <span class="hljs-attr">items</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> [
          ...ctx.items.slice(<span class="hljs-number">0</span>, e.index),
          ...ctx.items.slice(e.index + <span class="hljs-number">1</span>)
        ]
      })
    }
  }
);
</code></pre>
<p>Which matches the following visualization:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866550146/TfDNWm_60.png" alt="State Machine" /></p>
<p>There's basically 2 states:</p>
<ul>
<li><strong>Normal</strong>, in which you can <strong>add 1 shape</strong> at a time to the list of items.</li>
<li><strong>Turbo</strong>, in which you can <strong>add 3 shapes</strong> at a time to the list of items.</li>
</ul>
<p>In both states you can <strong>delete</strong> a shape (you pass the shape's list index to the event, e.g. by clicking on it).</p>
<p>To be able to undo/redo our mutations to the <code>items</code> context, we need to do a few things (taken from <a target="_blank" href="https://redux.js.org/recipes/implementing-undo-history/">The Redux Doc on Undo/Redo</a>):</p>
<h3 id="heading-handling-undo">Handling Undo</h3>
<ul>
<li>Remove the last element from the <strong>past</strong>.</li>
<li>Set the <strong>present</strong> to the element we removed in the previous step.</li>
<li>Insert the old <strong>present</strong> state at the beginning of the <strong>future</strong>.</li>
</ul>
<h3 id="heading-handling-redo">Handling Redo</h3>
<ul>
<li>Remove the first element from the <strong>future</strong>.</li>
<li>Set the <strong>present</strong> to the element we removed in the previous step.</li>
<li>Insert the old <strong>present</strong> state at the end of the <strong>past</strong>.</li>
</ul>
<h3 id="heading-handling-other-actions">Handling Other Actions</h3>
<ul>
<li>Insert the <strong>present</strong> at the end of the <strong>past</strong>.</li>
<li>Set the <strong>present</strong> to the new state after handling the action.</li>
<li>Clear the <strong>future</strong>.</li>
</ul>
<p>Here's what that looks like in our <em>Machine</em>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> editorMachine = Machine(
  {
    <span class="hljs-attr">id</span>: <span class="hljs-string">"editor"</span>,
    <span class="hljs-attr">context</span>: {
      <span class="hljs-comment">// Keep track of the past</span>
      <span class="hljs-attr">past</span>: [],

      <span class="hljs-comment">// Our present</span>
      <span class="hljs-attr">items</span>: [],

      <span class="hljs-comment">// Keep track of the future</span>
      <span class="hljs-attr">future</span>: []
    },
    <span class="hljs-attr">initial</span>: <span class="hljs-string">"normal"</span>,
    <span class="hljs-attr">states</span>: {
      <span class="hljs-attr">on</span>: {
        <span class="hljs-attr">DELETE_SHAPE</span>: {
          <span class="hljs-comment">// Update the past when we delete a shape</span>
          <span class="hljs-attr">actions</span>: [<span class="hljs-string">"updatePast"</span>, <span class="hljs-string">"deleteShape"</span>]
        },
        <span class="hljs-attr">UNDO</span>: {
          <span class="hljs-attr">actions</span>: [<span class="hljs-string">"undo"</span>]
        },
        <span class="hljs-attr">REDO</span>: {
          <span class="hljs-attr">actions</span>: [<span class="hljs-string">"redo"</span>]
        }
      },
      <span class="hljs-attr">normal</span>: {
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">TOGGLE_MODE</span>: <span class="hljs-string">"turbo"</span>,
          <span class="hljs-attr">ADD_SHAPE</span>: {
          <span class="hljs-comment">// Update the past when we add a shape</span>
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">"updatePast"</span>, <span class="hljs-string">"addShape"</span>]
          }
        }
      },
      <span class="hljs-attr">turbo</span>: {
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">TOGGLE_MODE</span>: <span class="hljs-string">"normal"</span>,
          <span class="hljs-attr">ADD_SHAPE</span>: {
            <span class="hljs-comment">// Update the past when we add 3 shapes</span>
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">"updatePast"</span>, <span class="hljs-string">"addThreeShapes"</span>]
          }
        }
      }
    },
  },
  {
    <span class="hljs-attr">actions</span>: {
      <span class="hljs-attr">addShape</span>: assign({
        <span class="hljs-attr">items</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> [...ctx.items, e.shape]
      }),
      <span class="hljs-attr">addThreeShapes</span>: assign({
        <span class="hljs-attr">items</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> [...ctx.items, e.shape, e.shape, e.shape]
      }),
      <span class="hljs-attr">deleteShape</span>: assign({
        <span class="hljs-attr">items</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> [
          ...ctx.items.slice(<span class="hljs-number">0</span>, e.index),
          ...ctx.items.slice(e.index + <span class="hljs-number">1</span>)
        ]
      }),

      <span class="hljs-comment">// # Handling Other Actions</span>
      <span class="hljs-attr">updatePast</span>: assign({
        <span class="hljs-comment">// 1. Insert the present at the end of the past.</span>
        <span class="hljs-attr">past</span>: <span class="hljs-function"><span class="hljs-params">ctx</span> =&gt;</span> [...ctx.past, ctx.items],

        <span class="hljs-comment">// 2. Set the present to the new state after handling the action.</span>
        <span class="hljs-comment">// ! This happens in the 3 specific actions above</span>

        <span class="hljs-comment">// 3. Clear the future.</span>
        <span class="hljs-attr">future</span>: []
      }),

      <span class="hljs-comment">// # Handling Undo</span>
      <span class="hljs-attr">undo</span>: assign(<span class="hljs-function"><span class="hljs-params">ctx</span> =&gt;</span> {
        <span class="hljs-keyword">const</span> previous = ctx.past[ctx.past.length - <span class="hljs-number">1</span>];

        <span class="hljs-comment">// 1. Remove the last element from the past.</span>
        <span class="hljs-keyword">const</span> newPast = ctx.past.slice(<span class="hljs-number">0</span>, ctx.past.length - <span class="hljs-number">1</span>);
        <span class="hljs-keyword">return</span> {
          <span class="hljs-attr">past</span>: newPast,

          <span class="hljs-comment">// 2. Set the present to the element we removed in step 1.</span>
          <span class="hljs-attr">items</span>: previous,

          <span class="hljs-comment">// 3. Insert the old present state at the beginning of the future.</span>
          <span class="hljs-attr">future</span>: [ctx.items, ...ctx.future]
        };
      }),

      <span class="hljs-comment">// # Handling Redo</span>
      <span class="hljs-attr">redo</span>: assign(<span class="hljs-function"><span class="hljs-params">ctx</span> =&gt;</span> {
        <span class="hljs-keyword">const</span> next = ctx.future[<span class="hljs-number">0</span>];

        <span class="hljs-comment">// 1. Remove the first element from the future.</span>
        <span class="hljs-keyword">const</span> newFuture = ctx.future.slice(<span class="hljs-number">1</span>);
        <span class="hljs-keyword">return</span> {

          <span class="hljs-comment">// 2. Set the present to the element we removed in step 1.</span>
          <span class="hljs-attr">items</span>: next,

          <span class="hljs-comment">// 3. Insert the old present state at the end of the past.</span>
          <span class="hljs-attr">past</span>: [...ctx.past, ctx.items],
          <span class="hljs-attr">future</span>: newFuture
        };
      })
    }
  }
);
</code></pre>
<p>And that's all! Now we have all the benefits of state machines combined with an undo/redo system on our extended state. We can craft a robust, complex editor (think of all the tools in the Photoshop toolbox) while keeping our undo/redo system simple!</p>
<p>Check out the <a target="_blank" href="https://codesandbox.io/s/xstate-undo-redo-i3eiu">CodeSandbox</a> for an implemented example.</p>
]]></content:encoded></item><item><title><![CDATA[Stateful Styles With XState and Styled System]]></title><description><![CDATA[You've probably seen a button like this one before:
<Button>Cool Button</Button>

One that has options:
<Button secondary>Secondary Cool Button</Button>

Maybe even more options:
<Button tertiary>Tertiary Cool Button</Button>

But what if I did this?...]]></description><link>https://blog.robertbroersma.com/stateful-styles-with-xstate-and-styled-system</link><guid isPermaLink="true">https://blog.robertbroersma.com/stateful-styles-with-xstate-and-styled-system</guid><category><![CDATA[React]]></category><category><![CDATA[xstate]]></category><category><![CDATA[styled-components]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Fri, 13 Mar 2020 15:17:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866553312/b8q02G6JH.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You've probably seen a button like this one before:</p>
<pre><code class="lang-jsx">&lt;Button&gt;Cool Button&lt;/Button&gt;
</code></pre>
<p>One that has options:</p>
<pre><code class="lang-jsx">&lt;Button secondary&gt;Secondary Cool Button&lt;/Button&gt;
</code></pre>
<p>Maybe even more options:</p>
<pre><code class="lang-jsx">&lt;Button tertiary&gt;Tertiary Cool Button&lt;/Button&gt;
</code></pre>
<p>But what if I did this?</p>
<pre><code class="lang-jsx">&lt;Button secondary tertiary&gt;Secondary? Cool Button&lt;/Button&gt;
</code></pre>
<p>That's probably not allowed. I guess we'll change the API to avoid that:</p>
<pre><code class="lang-jsx">&lt;Button variant=<span class="hljs-string">"secondary"</span>&gt;Secondary Cool Button&lt;/Button&gt;
</code></pre>
<p>This is kind of a state machine! Your <code>Button</code> can only be in one <code>variant</code> (state) at a time.</p>
<p>Here's what a parallel state machine (basically multiple independent state machines) would look like:</p>
<pre><code class="lang-jsx">&lt;Button variant=<span class="hljs-string">"secondary"</span> mode=<span class="hljs-string">"dark"</span>&gt;Dark Secondary Cool Button&lt;/Button&gt;
</code></pre>
<p>I've found that these kind of style props work very well with logical state machines. Check out the following example of a... thing:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866553312/b8q02G6JH.png" alt="Visualization of a State Machine" /></p>
<p>It's a parallel state machine with 3 sub machines:</p>
<ul>
<li>One machine that let's you change the shape:<ul>
<li>From Circle to Square</li>
<li>From Square to Diamond</li>
<li>From Square to Circle</li>
<li>From Diamond to Square</li>
</ul>
</li>
<li>One machine that let's you change the color:<ul>
<li>From Red to Blue</li>
<li>From Blue to Green</li>
<li>From Green to Red</li>
</ul>
</li>
<li>One machine that let's you change the size:<ul>
<li>From Small to Big</li>
<li>From Big to Small</li>
</ul>
</li>
</ul>
<p>If we want to craft some stateful styles for this thing, we'd need a component with an API like this:</p>
<pre><code class="lang-jsx">&lt;Thing shape=<span class="hljs-string">"circle|square|diamond"</span> color=<span class="hljs-string">"red|blue|green"</span> size=<span class="hljs-string">"small|big"</span> /&gt;
</code></pre>
<p>You can implement it however you like, but what I like to do is use <a target="_blank" href="https://styled-system.com/variants"><code>styled-system</code>'s <code>variant</code> API</a>, because it maps nicely to the state machines we defined:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> styled <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-components'</span>
<span class="hljs-keyword">import</span> { variant } <span class="hljs-keyword">from</span> <span class="hljs-string">'styled-system'</span>

<span class="hljs-keyword">const</span> Thing = styled(
  variant({
    <span class="hljs-attr">prop</span>: <span class="hljs-string">'shape'</span>,
    <span class="hljs-attr">variants</span>: {
      <span class="hljs-attr">square</span>: {
        <span class="hljs-comment">/** Make it square */</span>
      },
      <span class="hljs-attr">circle</span>: {
        <span class="hljs-comment">/** Make it circular */</span>
      },
      <span class="hljs-attr">diamond</span>: {
        <span class="hljs-comment">/** Make it a diamond */</span>
      },
    },
  }),
  variant({
    <span class="hljs-attr">prop</span>: <span class="hljs-string">'color'</span>,
    <span class="hljs-comment">// ...</span>
  }),
  variant({
    <span class="hljs-attr">prop</span>: <span class="hljs-string">'size'</span>,
    <span class="hljs-comment">// ...</span>
  })
)
</code></pre>
<p>(You can use it with either Emotion or Styled Components)</p>
<p>Now to wire it up to our state machine using <code>xstate</code> and <code>@xstate/react</code></p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [state, send] = useMachine(shapeMachine);

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Shape</span> {<span class="hljs-attr">...state.value</span>} /&gt;</span></span>
}
</code></pre>
<p>Ta-da! A little explanation:
In case of a hierarchical or parallel state machine, ours being the latter, <code>state.value</code> contains an object representation of our current state (check <a target="_blank" href="https://xstate.js.org/docs/guides/states.html">the docs</a> for more info). Our state could look something like this:</p>
<pre><code class="lang-json"><span class="hljs-comment">// state.value</span>
{
  shape: <span class="hljs-string">"circle"</span>,
  color: <span class="hljs-string">"red"</span>,
  size: <span class="hljs-string">"small"</span>
}
</code></pre>
<p>Which happens to look exactly like our component's prop interface! Of course you can also do <em>this</em> if you want your code to be a bit more explicit and readable:</p>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [state, send] = useMachine(shapeMachine);

  <span class="hljs-keyword">const</span> { shape, size, color } = state.value
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Shape</span> <span class="hljs-attr">shape</span>=<span class="hljs-string">{shape}</span> <span class="hljs-attr">size</span>=<span class="hljs-string">{size}</span> <span class="hljs-attr">color</span>=<span class="hljs-string">{color}</span> /&gt;</span></span>
}
</code></pre>
<p>Here's a <a target="_blank" href="https://codesandbox.io/s/stateful-cssinjs-6mpxe">CodeSandbox</a> with a fully working example.</p>
]]></content:encoded></item><item><title><![CDATA[Building a Generic State Machine for Form Handling Using XState]]></title><description><![CDATA[If you're a computer scientist or follow @davidkpiano you've probably heard about state machines.
They are awesome.
Here's an example of how to use one for form handling!
Our designer says the form should look like this:

From this concept we can ded...]]></description><link>https://blog.robertbroersma.com/building-a-generic-state-machine-for-form-handling-using-xstate</link><guid isPermaLink="true">https://blog.robertbroersma.com/building-a-generic-state-machine-for-form-handling-using-xstate</guid><category><![CDATA[xstate]]></category><category><![CDATA[state-machines]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Sun, 08 Mar 2020 13:32:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866722295/ju6OVPY2y.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you're a computer scientist or follow @davidkpiano you've probably heard about state machines.</p>
<p>They are awesome.</p>
<p>Here's an example of how to use one for form handling!</p>
<p>Our designer says the form should look like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866722295/ju6OVPY2y.png" alt="4 App States: Editing, Submitting, Error, Success" /></p>
<p>From this concept we can deduce four "states":</p>
<ol>
<li>Editing</li>
<li>Submitting</li>
<li>Error</li>
<li>Success</li>
</ol>
<p>Let's define the states in a machine:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formMachine = Machine({
  <span class="hljs-comment">// We'll start in the editing state</span>
  <span class="hljs-attr">initial</span>: <span class="hljs-string">'editing'</span>,
  <span class="hljs-attr">states</span>: {
    <span class="hljs-attr">editing</span>: {},
    <span class="hljs-attr">submitting</span>: {},
    <span class="hljs-attr">error</span>: {},
    <span class="hljs-attr">success</span>: {},
  },
})
</code></pre>
<h2 id="heading-editing-state">Editing State</h2>
<p>While in the editing state, we can do 2 things:</p>
<ul>
<li>Type in the fields. We stay in the same state. We of course also want the input to be saved.</li>
<li>Submit the form. We transition to the <em>submitting</em> state.</li>
</ul>
<p>Let's define the transitions and actions:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formMachine = Machine(
  {
    <span class="hljs-attr">initial</span>: <span class="hljs-string">'editing'</span>,
    <span class="hljs-comment">// Context contains all our infinite state, like text input!</span>
    <span class="hljs-attr">context</span>: {
      <span class="hljs-attr">values</span>: {},
    },
    <span class="hljs-attr">states</span>: {
      <span class="hljs-attr">editing</span>: {
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">CHANGE</span>: {
            <span class="hljs-comment">// Stay in the same state</span>
            <span class="hljs-attr">target</span>: <span class="hljs-string">''</span>,

            <span class="hljs-comment">// Execute the onChange action</span>
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">'onChange'</span>],
          },
          <span class="hljs-attr">SUBMIT</span>: <span class="hljs-string">'submitting'</span>,
        },
      },
      <span class="hljs-attr">submitting</span>: {},
      <span class="hljs-attr">error</span>: {},
      <span class="hljs-attr">success</span>: {},
    },
  },
  {
    <span class="hljs-attr">actions</span>: {
      <span class="hljs-comment">// Assign</span>
      <span class="hljs-attr">onChange</span>: assign({
        <span class="hljs-attr">values</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> ({
          ...ctx.values,
          [e.key]: e.value,
        }),
      }),
    },
  },
)
</code></pre>
<h2 id="heading-submitting-state">Submitting State</h2>
<p>After submitting the form, our life could go one of two ways:</p>
<ul>
<li>The submission is succesful, we move to the <em>success</em> state.</li>
<li>The submission failed, we move to the <em>error</em> state.</li>
</ul>
<p>To keep our machine generic, we'll leave the whatever happens during the submission up to the consumer of the machine by invoking a service. Allowing the consumer to pass in their own service (See <a target="_blank" href="https://xstate.js.org/docs/guides/communication.html">Invoking Services</a>). Be it frontend validation, backend validation or no validation, we don't care! The only thing we'll do is transition based on a succesful or unsuccesful response, storing the error data on an unsuccesful response.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formMachine = Machine(
  {
    <span class="hljs-attr">initial</span>: <span class="hljs-string">'editing'</span>,
    <span class="hljs-attr">context</span>: {
      <span class="hljs-attr">values</span>: {},
      <span class="hljs-attr">errors</span>: {},
    },
    <span class="hljs-attr">states</span>: {
      <span class="hljs-attr">editing</span>: {
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">CHANGE</span>: {
            <span class="hljs-attr">target</span>: <span class="hljs-string">''</span>,
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">'onChange'</span>],
          },
          <span class="hljs-attr">SUBMIT</span>: <span class="hljs-string">'submitting'</span>,
        },
      },
      <span class="hljs-attr">submitting</span>: {
        <span class="hljs-attr">invoke</span>: {
          <span class="hljs-attr">src</span>: <span class="hljs-string">'onSubmit'</span>,
          <span class="hljs-comment">// Move to the success state onDone</span>
          <span class="hljs-attr">onDone</span>: <span class="hljs-string">'success'</span>,
          <span class="hljs-attr">onError</span>: {
            <span class="hljs-comment">// Move to the error state onError</span>
            <span class="hljs-attr">target</span>: <span class="hljs-string">'error'</span>,

            <span class="hljs-comment">// Execute onChange action</span>
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">'onError'</span>],
          },
        },
      },
      <span class="hljs-attr">error</span>: {},
      <span class="hljs-attr">success</span>: {},
    },
  },
  {
    <span class="hljs-attr">actions</span>: {
      <span class="hljs-attr">onChange</span>: assign({
        <span class="hljs-attr">values</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> ({
          ...ctx.values,
          [e.key]: e.value,
        }),
      }),
      <span class="hljs-attr">onError</span>: assign({
        <span class="hljs-attr">errors</span>: <span class="hljs-function">(<span class="hljs-params">_ctx, e</span>) =&gt;</span> e.data,
      }),
    },
  },
)
</code></pre>
<h1 id="heading-error-state">Error State</h1>
<p>Uh-Oh! We've stumbled upon a few errors. The user can now do two things:</p>
<ul>
<li>Change the inputs.</li>
<li>Submit the form again.</li>
</ul>
<p>Hey, these are the same things we could do in the <em>editing</em> state! Come to think of it, this state is actually pretty similar to editing, only there are some errors in the screen. We <em>could</em> now move the transitions up to the root state, allowing us to ALWAYS change the inputs and ALWAYS submit the form, but obviously we don't want that! We don't want the user to edit the form <em>while</em> it's submitting. What we <em>can</em> do is make the editing state hierarchical with 2 substates: <em>pristine</em> (not submitted) and <em>error</em> (submitted and wrong):</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formMachine = Machine(
  {
    <span class="hljs-attr">initial</span>: <span class="hljs-string">'editing'</span>,
    <span class="hljs-attr">context</span>: {
      <span class="hljs-attr">values</span>: {},
      <span class="hljs-attr">errors</span>: {},
    },
    <span class="hljs-attr">states</span>: {
      <span class="hljs-attr">editing</span>: {
        <span class="hljs-comment">// We start the submachine in the pristine state</span>
        <span class="hljs-attr">initial</span>: <span class="hljs-string">'pristine'</span>,
        <span class="hljs-comment">// These transitions are available in all substates</span>
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">CHANGE</span>: {
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">'onChange'</span>],
          },
          <span class="hljs-attr">SUBMIT</span>: <span class="hljs-string">'submitting'</span>,
        },
        <span class="hljs-comment">// The 2 substates</span>
        <span class="hljs-attr">states</span>: {
          <span class="hljs-attr">pristine</span>: {},
          <span class="hljs-attr">error</span>: {},
        },
      },
      <span class="hljs-attr">submitting</span>: {
        <span class="hljs-attr">invoke</span>: {
          <span class="hljs-attr">src</span>: <span class="hljs-string">'onSubmit'</span>,
          <span class="hljs-attr">onDone</span>: <span class="hljs-string">'success'</span>,
          <span class="hljs-attr">onError</span>: {
            <span class="hljs-comment">// Note that we now need to point to the error substate of editing</span>
            <span class="hljs-attr">target</span>: <span class="hljs-string">'editing.error'</span>,
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">'onError'</span>],
          },
        },
      },
      <span class="hljs-attr">success</span>: {},
    },
  },
  {
    <span class="hljs-attr">actions</span>: {
      <span class="hljs-attr">onChange</span>: assign({
        <span class="hljs-attr">values</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> ({
          ...ctx.values,
          [e.key]: e.value,
        }),
      }),
      <span class="hljs-attr">onError</span>: assign({
        <span class="hljs-attr">errors</span>: <span class="hljs-function">(<span class="hljs-params">_ctx, e</span>) =&gt;</span> e.data,
      }),
    },
  },
)
</code></pre>
<h2 id="heading-success-state">Success State</h2>
<p>We did it! A succesful submission. According to the designs there's only one thing left to do here:</p>
<ul>
<li>Add another form submission.</li>
</ul>
<p>Easy peasy, we just transition back to the initial form!</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> formMachine = Machine(
  {
    <span class="hljs-attr">initial</span>: <span class="hljs-string">'editing'</span>,
    <span class="hljs-attr">context</span>: {
      <span class="hljs-attr">values</span>: {},
      <span class="hljs-attr">errors</span>: {},
    },
    <span class="hljs-attr">states</span>: {
      <span class="hljs-attr">editing</span>: {
        <span class="hljs-attr">initial</span>: <span class="hljs-string">'pristine'</span>,
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">CHANGE</span>: {
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">'onChange'</span>],
          },
          <span class="hljs-attr">SUBMIT</span>: <span class="hljs-string">'submitting'</span>,
        },
        <span class="hljs-attr">states</span>: {
          <span class="hljs-attr">pristine</span>: {
            <span class="hljs-comment">// This is up to you, but I felt like the form needed to be cleared before receiving a new submission</span>
            <span class="hljs-attr">entry</span>: [<span class="hljs-string">'clearForm'</span>],
          },
          <span class="hljs-attr">error</span>: {},
        },
      },
      <span class="hljs-attr">submitting</span>: {
        <span class="hljs-attr">invoke</span>: {
          <span class="hljs-attr">src</span>: <span class="hljs-string">'onSubmit'</span>,
          <span class="hljs-attr">onDone</span>: <span class="hljs-string">'success'</span>,
          <span class="hljs-attr">onError</span>: {
            <span class="hljs-attr">target</span>: <span class="hljs-string">'editing.error'</span>,
            <span class="hljs-attr">actions</span>: [<span class="hljs-string">'onError'</span>],
          },
        },
      },
      <span class="hljs-attr">success</span>: {
        <span class="hljs-attr">on</span>: {
          <span class="hljs-attr">AGAIN</span>: <span class="hljs-string">'editing'</span>,
        },
      },
    },
  },
  {
    <span class="hljs-attr">actions</span>: {
      <span class="hljs-attr">onChange</span>: assign({
        <span class="hljs-attr">values</span>: <span class="hljs-function">(<span class="hljs-params">ctx, e</span>) =&gt;</span> ({
          ...ctx.values,
          [e.key]: e.value,
        }),
      }),
      <span class="hljs-attr">clearForm</span>: assign({
        <span class="hljs-attr">values</span>: {},
        <span class="hljs-attr">errors</span>: {},
      }),
      <span class="hljs-attr">onError</span>: assign({
        <span class="hljs-attr">errors</span>: <span class="hljs-function">(<span class="hljs-params">_ctx, e</span>) =&gt;</span> e.data,
      }),
    },
  },
)
</code></pre>
<p>And that's it! A basic generic state machine that you could use on "any" form using any validation library or method that you want.</p>
<p>Checkout the interactive visualization <a target="_blank" href="https://xstate.js.org/viz/?gist=f0bad5c0d06f1fbead95be431e6dc8b7">here</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659866723628/Arfy3y-M7.png" alt="Form State Machine" /></p>
<p>For the full machine code and an implementation in <code>React</code> using <code>@xstate/react</code>, check out <a target="_blank" href="https://codesandbox.io/s/busy-lederberg-3c37e">this CodeSandbox</a></p>
<p>UI is implemented using the awesome <a target="_blank" href="https://chakra-ui.com/">Chakra UI</a></p>
]]></content:encoded></item><item><title><![CDATA[Multiple Authentication Types in AWS Amplify]]></title><description><![CDATA[The goal is to create a backend that allows us to:

Signup and login
Create and manage posts through a GraphQL API
Enable everyone (also unauthenticated users) to READ all posts
Prevent users from editing posts by others

We run the following command...]]></description><link>https://blog.robertbroersma.com/multiple-authentication-types-in-aws-amplify</link><guid isPermaLink="true">https://blog.robertbroersma.com/multiple-authentication-types-in-aws-amplify</guid><category><![CDATA[amplify]]></category><category><![CDATA[GraphQL]]></category><category><![CDATA[aws-amplify]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Mon, 02 Mar 2020 17:34:52 GMT</pubDate><content:encoded><![CDATA[<p>The goal is to create a backend that allows us to:</p>
<ol>
<li>Signup and login</li>
<li>Create and manage posts through a GraphQL API</li>
<li>Enable everyone (also unauthenticated users) to READ all posts</li>
<li>Prevent users from editing posts by others</li>
</ol>
<p>We run the following commands to setup our Amplify project:</p>
<pre><code class="lang-cli">amplify init
? Initialize amplify with your prefered settings
</code></pre>
<pre><code class="lang-cli">amplify add auth
? Do you want to use the default authentication and security configuration
Default configuration

? How do you want users to be able to sign in?
Username

? Do you want to configure advanced settings?
No, I am done.

# I went with all the defaults. Configure however you'd like!
</code></pre>
<pre><code class="lang-cli">amplify add api

? Please select from one of the below mentioned services: 
GraphQL

? Provide API name: 
amplifyPosts

? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project.
# Using cognito user pool for signing up and logging in users

? Do you want to configure advanced settings for the GraphQL API 
Yes, I want to make some additional changes.

? Configure additional auth types? 
Yes

? Choose the additional authorization types you want to configure for the API 
API key
# A public API Key is needed to read ALL the posts

API key configuration
? Enter a description for the API key: 
amplifyPosts

? After how many days from now the API key should expire (1-365): 
7

? Configure conflict detection? 
No

? Do you have an annotated GraphQL schema? 
No

? Do you want a guided schema creation? 
Yes

? What best describes your project: 
Single object with fields (e.g., “Todo” with ID, name, description)

? Do you want to edit the schema now? 
Yes
# This is where you copy and paste the schema below

✔ GraphQL schema compiled successfully.
</code></pre>
<pre><code class="lang-cli">amplify add codegen

? Choose the code generation language target 
javascript

? Enter the file name pattern of graphql queries, mutations and subscriptions
src/graphql/**/*.js

? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions 
Yes

? Enter maximum statement depth [increase from default if your schema is deeply nested] 
2

✔ Generated GraphQL operations successfully and saved at src/graphql
</code></pre>
<p>The schema:</p>
<pre><code class="lang-graphql">type Post
  @model
  @auth(
    rules: [
      # The owner is allowed to do everything
      { allow: owner }
      # The public is only allowed to read posts
      { allow: public, operations: [read] }
    ]
  ) {
  id: ID!
  title: String!
  content: String
  owner: String
}
</code></pre>
<p>At this point we can either run <code>amplify push</code> to deploy our backend, or <code>amplify mock</code> to mock our backend locally (this still required you to deploy the auth module first, but the <code>mock</code> command will guide you through that!)</p>
<p>At this point, add some users and posts to the backend by writing some mutations in the Amplify Console (or in the GraphiQL client at http://localhost:20002 if you used <code>amplify mock</code>).</p>
<p>After you <code>push</code> or <code>mock</code> your backend, Amplify will generate some files for you in your specified src directory:</p>
<ol>
<li><code>aws-exports.js</code> - A configuration file for our frontend to communicate with our backend</li>
<li><code>src/graphql/{mutations,queries,subscriptions}.js</code> - A starting point for GraphQL queries so we don't have to write them ourselves (you can write more optimized ones tho if you want!)</li>
</ol>
<p>Now's the time to consume the backend in our frontend.</p>
<p>Install <code>aws-amplify</code> and whatever aws package that fits your framework (We use <code>react</code> and <code>aws-amplify-react</code>):</p>
<pre><code class="lang-cli">yarn add aws-amplify aws-amplify-react
</code></pre>
<p>Somewhere high up in our component tree, we configure <code>Amplify</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Amplify <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-amplify'</span>
<span class="hljs-keyword">import</span> awsconfig <span class="hljs-keyword">from</span> <span class="hljs-string">'../aws-exports'</span>

Amplify.configure(awsconfig)
</code></pre>
<p>To query for the posts created by the <em>authenticated user</em>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { API, graphqlOperation } <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-amplify'</span>
<span class="hljs-keyword">import</span> { withAuthenticator } <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-amplify-react'</span>
<span class="hljs-keyword">import</span> { listPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">'../graphql/queries'</span>

<span class="hljs-keyword">const</span> MyPostsScreen = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [myPosts, setMyPosts] = useState([])

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPosts</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> API.graphql(graphqlOperation(listPosts))
      setMyPosts(data.listPosts.items)
    }

    getPosts()
  }, [])

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      My Posts:
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        {myPosts.map(post =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Post</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{post.id}</span> <span class="hljs-attr">post</span>=<span class="hljs-string">{post}</span> /&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}

<span class="hljs-comment">// withAuthenticator wraps our components in a signup/login, which makes sure we are logged in when accessing this component!</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AuthenticatedMyPostsScreen = withAuthenticator(MyPostsScreen)
</code></pre>
<p>Now, to query for ALL posts, all we need to do is switch the authMode to <code>API_KEY</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> { API } <span class="hljs-keyword">from</span> <span class="hljs-string">'aws-amplify'</span>
<span class="hljs-keyword">import</span> { listPosts } <span class="hljs-keyword">from</span> <span class="hljs-string">'../graphql/queries'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AllPostsScreen = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [allPosts, setAllPosts] = useState([])

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPosts</span>(<span class="hljs-params"></span>) </span>{
      <span class="hljs-comment">// Switch authMode to API_KEY</span>
      <span class="hljs-keyword">const</span> { data } = <span class="hljs-keyword">await</span> API.graphql({ 
        <span class="hljs-attr">query</span>: listPosts, 
        <span class="hljs-attr">authMode</span>: <span class="hljs-string">'API_KEY'</span>,
      })
      setPosts(data.listPosts.items)
    }

    getPosts()
  }, [])

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      All Posts:
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        {allPosts.map(post =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">Post</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{post.id}</span> <span class="hljs-attr">post</span>=<span class="hljs-string">{post}</span> /&gt;</span>
        ))}
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  )
}
</code></pre>
<p>That's it! Because of the way our GraphQL schema uses the <code>@auth</code> directive, it is still impossible to edit posts that you don't own. Trying to run a mutation using <code>authMode: 'API_KEY'</code> will result in an <code>Unauthorized</code> error.</p>
<p>It took me a while to figure out you need to switch the <code>authMode</code>. If there's a better or easier way to do this, I'd love to hear about it! </p>
<p>Cheers.</p>
]]></content:encoded></item><item><title><![CDATA[Quick Tip - Memoizing change handlers in React Components]]></title><description><![CDATA[Let's consider a basic form with a controlled component in react:
class Form extends React.Component {
  state = {
    value: '',
  };

  handleChange = e => {
    this.setState({
      value: e.target.value,
    });
  };

  render() {
    return (
 ...]]></description><link>https://blog.robertbroersma.com/quick-tip-memoizing-change-handlers-in-react-components</link><guid isPermaLink="true">https://blog.robertbroersma.com/quick-tip-memoizing-change-handlers-in-react-components</guid><category><![CDATA[React]]></category><category><![CDATA[Memoization]]></category><category><![CDATA[optimization]]></category><dc:creator><![CDATA[Robert]]></dc:creator><pubDate>Mon, 01 Oct 2018 15:58:49 GMT</pubDate><content:encoded><![CDATA[<p>Let's consider a basic form with a controlled component in <code>react</code>:</p>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Form</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{
  state = {
    <span class="hljs-attr">value</span>: <span class="hljs-string">''</span>,
  };

  handleChange = <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
    <span class="hljs-built_in">this</span>.setState({
      <span class="hljs-attr">value</span>: e.target.value,
    });
  };

  render() {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">InputComponent</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.value}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleChange}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
  }
}
</code></pre>
<p>We keep a state, pass the value to our <code>InputComponent</code>, and update the value with the value we get from it.</p>
<p>Now consider this larger form. I like to use this arrow-function-that-returns-another-arrow-function (what do you call this?) syntax for brevity and to not have to repeat myself with multiple change handlers.</p>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BiggerForm</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{
  state = {
    <span class="hljs-attr">a</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">b</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">c</span>: <span class="hljs-string">''</span>,
  };

  handleChange = <span class="hljs-function"><span class="hljs-params">key</span> =&gt;</span> <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
    <span class="hljs-built_in">this</span>.setState({
      [key]: e.target.value,
    });
  };

  render() {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">InputComponent</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.a}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleChange(</span>'<span class="hljs-attr">a</span>')} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">InputComponent</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.b}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleChange(</span>'<span class="hljs-attr">b</span>')} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">InputComponent</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.c}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleChange(</span>'<span class="hljs-attr">c</span>')} /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
  }
}
</code></pre>
<p>Looks easy, right? The problem with this is that <code>this.handleChange()</code> will create a new function every time it is called. Meaning everytime BiggerForm re-renders, all the <code>InputComponents</code> will re-render. Meaning EVERYTHING will re-render on EVERY keystroke. You can imagine what this would do to a huge form.</p>
<p>Now what we could do is either split <code>handleChange</code> into specific change handlers, e.g. <code>handleChangeA</code>, <code>handleChangeB</code>, <code>handleChangeC</code>, and this would solve our issue. But this is a lot of repetition, and considering we are considering huge forms; a lot of tedious work.</p>
<p>Luckily there's this thing called memoization! Which in short is a caching mechanism for our functions. Sounds fancy, but all it does is remember which arguments yield what result when calling a function. When the function is called again with the same arguments, it will not execute the function, but just return the same result. In our example:</p>
<pre><code class="lang-jsx"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MemoizeForm</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{
  state = {
    <span class="hljs-attr">a</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">b</span>: <span class="hljs-string">''</span>,
    <span class="hljs-attr">c</span>: <span class="hljs-string">''</span>,
  };

  handleChange = memoize(<span class="hljs-function"><span class="hljs-params">key</span> =&gt;</span> <span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
    <span class="hljs-built_in">this</span>.setState({
      [key]: e.target.value,
    });
  });

  render() {
    <span class="hljs-keyword">return</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">InputComponent</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.a}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleChange(</span>'<span class="hljs-attr">a</span>')} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">InputComponent</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.b}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleChange(</span>'<span class="hljs-attr">b</span>')} /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">InputComponent</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{this.state.c}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleChange(</span>'<span class="hljs-attr">c</span>')} /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    )
  }
}
</code></pre>
<p>That was easy! In this example, on the first render of <code>MemoizeForm</code>, the <code>handleChange</code> function is called for every <code>InputComponent</code> with their specific key as an argument. Whenever <code>MemoizeForm</code> re-renders, <code>handleChange</code> is called again. However, since it's called with the same argument as before, the memoization mechanism returns the same function (with the same reference), and the <code>InputComponent</code> is not re-rendered (unless the value is changed ofcourse!).</p>
<p>🎉</p>
<p>P.S. Any memoization library will do, I like to use <a target="_blank" href="https://github.com/caiogondim/fast-memoize.js/">fast-memoize</a></p>
<p>-- EDIT --</p>
<p>I've recently learned that <code>event.target</code> contains a lot more stuff! Using hooks you could just do:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [state, setState] = useState(initialValues)

<span class="hljs-keyword">const</span> handleChange = useCallback(<span class="hljs-function"><span class="hljs-params">e</span> =&gt;</span> {
  setState(<span class="hljs-function"><span class="hljs-params">values</span> =&gt;</span> ({ ...values, [e.target.name]: e.target.value }))
}), [])
</code></pre>
]]></content:encoded></item></channel></rss>