<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: John Kazer</title>
    <description>The latest articles on DEV Community by John Kazer (@johnkazer).</description>
    <link>https://dev.to/johnkazer</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F92212%2F9b369ec3-659c-4f1a-80f9-24a9b0d95102.jpeg</url>
      <title>DEV Community: John Kazer</title>
      <link>https://dev.to/johnkazer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/johnkazer"/>
    <language>en</language>
    <item>
      <title>Async functionality - currying that makes sense</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Thu, 03 Nov 2022 02:46:02 +0000</pubDate>
      <link>https://dev.to/johnkazer/async-functionality-currying-that-makes-sense-58d2</link>
      <guid>https://dev.to/johnkazer/async-functionality-currying-that-makes-sense-58d2</guid>
      <description>&lt;p&gt;There are a range of ways in JavaScript to handle or express delayed activity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Callbacks (pass a single function to be used later)&lt;/li&gt;
&lt;li&gt;Promises&lt;/li&gt;
&lt;li&gt;Async/Await&lt;/li&gt;
&lt;li&gt;setTimeout&lt;/li&gt;
&lt;li&gt;Subscription&lt;/li&gt;
&lt;li&gt;Push notifications&lt;/li&gt;
&lt;li&gt;etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another way of providing delayed functionality is "currying". Regardless of the many tutorials about how currying works, the main point is to handle a delay in the application of functionality. I just use the Ramda &lt;code&gt;curry&lt;/code&gt; function and don't worry about the mechanics.&lt;/p&gt;

&lt;p&gt;In fact, currying can work quite well in an asynchronous environment because the delay is fundamental. You may often be subject to waiting for all the information before executing full functionality.&lt;/p&gt;

&lt;p&gt;An example might be pre-configuring a button in React, but needing to receive a user selection from a drop-down that completes the configuration before adding it to the UI. The user's selection might define the button's label text for example.&lt;/p&gt;

&lt;p&gt;Another example is making use of the difference between configuring how to process an array, and actually doing the processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;curry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ramda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listOfData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;// the core function expressing the functionality we want&lt;/span&gt;
&lt;span class="c1"&gt;// but wrapped by the 'curry' function&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processMyList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;configParam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;configParam&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;listItem&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchedConfigParam&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;someAsyncronousFunction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// fetches a number&lt;/span&gt;
&lt;span class="c1"&gt;// using 'curry' creates a new function we can use later&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementListOfNumbers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;processMyList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchedConfigParam&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// provide the configParam arg&lt;/span&gt;
&lt;span class="c1"&gt;// do the processing&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processedList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;listOfData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;incrementListOfNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// provide the listItem arg&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>functional</category>
      <category>async</category>
      <category>curry</category>
    </item>
    <item>
      <title>NPM - too much of a good thing?</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Tue, 23 Mar 2021 22:23:32 +0000</pubDate>
      <link>https://dev.to/johnkazer/npm-too-much-of-a-good-thing-2ncm</link>
      <guid>https://dev.to/johnkazer/npm-too-much-of-a-good-thing-2ncm</guid>
      <description>&lt;p&gt;(cover pic &lt;a href="https://unsplash.com/@henleydesign?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;unsplash&lt;/a&gt;)&lt;br&gt;
A thought occurred to me today when I was considering &lt;a href="https://purelyfunctional.tv/issues/purelyfunctional-tv-newsletter-418-clojures-buzz/"&gt;an article by Eric Normand&lt;/a&gt; on the different moods or "buzz" that languages generate in the dev community. His discussion point is that JavaScript generates enormous interest, obvious activity and "buzz", whilst a language like Clojure is used extensively but not talked about.&lt;/p&gt;

&lt;p&gt;His take was that this is just fine, as Clojure is used by many for building apps (front and backend along with enterprise tools) - but it is very stable and therefore doesn't automatically generate "buzz" about itself... There are few changes to shout about and discuss.&lt;/p&gt;

&lt;p&gt;The thought that occurred to me was actually that maybe the JavaScript community generates &lt;em&gt;too much&lt;/em&gt; buzz, and spends &lt;em&gt;too much&lt;/em&gt; time developing new libraries on npm. If we just got on with the libraries we already have and spend less time gold-plating things and learning new stuff we'd have more energy to spend creating more and better quality products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--M4RitnU1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69n0w8zf55r6gr769jua.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M4RitnU1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/69n0w8zf55r6gr769jua.jpg" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
(pic &lt;a href="https://unsplash.com/@teveir?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;unsplash&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;There is indeed a lot to be said for an active community and the potential for an existing library out of the thousands to solve your problem very specifically.&lt;/p&gt;

&lt;p&gt;As an example, perhaps the differences between Vue and React or Svelte (Angular and this or that) don't really warrant their on-going segregation of the resulting apps and dev communities? Is it actually, overall, less efficient and resulting in lower-quality apps over time?&lt;/p&gt;

&lt;p&gt;(this might be a bad example, as I don't really want to discuss the merits of frameworks per se, but use their existence as an example that applies to npm in general)&lt;/p&gt;

&lt;p&gt;I &lt;em&gt;know&lt;/em&gt; that it can be argued that one or the other library/framework/whatever is better in certain circumstances than the others - but is that &lt;em&gt;really&lt;/em&gt; true and do you &lt;em&gt;really&lt;/em&gt; gain sufficient benefit compared to the effort of complicated build processes, maintenance, updates and on-boarding of new staff? Not to say the opportunity cost from barriers to knowledge sharing between teams using different libraries/frameworks regarding architectures and domain solutions.&lt;/p&gt;

&lt;p&gt;Perhaps if there was less "buzz" and more "do" in the JavaScript community, we'd have higher quality engineering and more apps?&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>npm</category>
      <category>healthydebate</category>
    </item>
    <item>
      <title>Refactoring a horrible function - thought processes</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Sun, 21 Feb 2021 22:56:49 +0000</pubDate>
      <link>https://dev.to/johnkazer/refactoring-a-horrible-function-thought-processes-3m8i</link>
      <guid>https://dev.to/johnkazer/refactoring-a-horrible-function-thought-processes-3m8i</guid>
      <description>&lt;p&gt;How to think differently about your code, via an example of how I rescued myself from a horrible function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL:DR&lt;/strong&gt; - Processing different data types separately and thinking hard about the key actions or fundamental operations you want to apply to data really do make for simpler code. In this example, I turned a horrible function with 5 nested forEach loops (and 3 auxillary functions) into 3 simple functions using 3 maps and a single forEach (and 2 auxillary functions).&lt;/p&gt;

&lt;h1&gt;
  
  
  The general problem
&lt;/h1&gt;

&lt;p&gt;I won't give a full description of how the initial function works, hopefully the general idea will be enough. And why would you pollute your mind with the effort?! Also, the refactoring should make the aim much clearer.&lt;/p&gt;

&lt;p&gt;The core problem is one of converting the result of searching in a narrow but deep set of data trees into a simple UI for selecting specific search results with a checkbox. I was using a useful library &lt;a href="https://github.com/krisk/Fuse"&gt;fuse&lt;/a&gt; to run the search and so to some degree was limited by the way it reported results (I did make some customizations).&lt;/p&gt;

&lt;p&gt;Steps I wanted to implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build some UI from a set of simple trees of HTMLElements representing the search results for users to select&lt;/li&gt;
&lt;li&gt;Only add to the simple trees those parts of the data trees that where positive search results&lt;/li&gt;
&lt;li&gt;Loop through each set of search results relevant to each data tree&lt;/li&gt;
&lt;li&gt;Loop through each data tree&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The basic data structure to be searched was an array with elements like the following. The search targets where the value keys of the Class3Name children:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;colName:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class1Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Energy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;colName:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class2Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Electricity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="err"&gt;colName:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Class3Name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Grid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="err"&gt;colName:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"factor"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="err"&gt;value:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"United Kingdom grid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="err"&gt;children:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;few&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;more&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;nested&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;levels&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;followed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;by&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
                                &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;objects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;some&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;data&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;details&lt;/span&gt;&lt;span class="w"&gt;
                            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which was augmented by some search result "matches" that gave me arrays of the indexes of tree children that lead to positive search results.&lt;/p&gt;

&lt;p&gt;So whilst trying to get my head around the complicated data structures I had, and contemplating the direct creation of UI, I created a rather imperative solution that successfully did everything at once. I also wrote a small essay of comments to try and explain what on earth was going on.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Horrible Function
&lt;/h1&gt;

&lt;p&gt;The following code is the eponymous "horrible function", slightly augmented with &lt;a href="https://jsdoc.app/about-getting-started.html"&gt;jsDocs&lt;/a&gt; types (see end of article). It also uses some helper functions, not included for brevity.&lt;/p&gt;

&lt;p&gt;The trap that I fell into was trying to manipulate the data directly to create UI. It is imperative code that defines how I want the data to be processed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;treeForUI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buildTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchResultsArray&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// will be added to the DOM&lt;/span&gt;
&lt;span class="cm"&gt;/**
     * 
     * The data arg will be an array of branches, each of which contain one or more positive search results (matches).
     * The problem is that it isn't known which part(s) of the branch was an actual result. So we use the information in the match object.
     * The match object contains an array of the specific results and their associated paths along their branch. The paths are described in two complementary ways.
     * Recall that a branch has one or more children and multiple levels, hence many 'leaves' or potential search targets.
     * Firstly, the sequence of search keys from creating the search library's object are re-used, in match.key.path.
     * Secondly the sequence of indexes in each 'children' array that describes the "route" to a search result is in match.path.
     * Therefore for each match in a branch, we want to drill down into the branch using the specific route through the path described by the search results.
     * @param { searchResult[] } data 
     * @return { HTMLDivElement }
     */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildTree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/** @type { HTMLElement } */&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newLevelOneNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createEfHtmlTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="cm"&gt;/** @type { HTMLElement } */&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newLevelOneNode&lt;/span&gt;
            &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/** @type { match } */&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="cm"&gt;/** @type { branch } */&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currentBranch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;
                &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// + 1 because have already handled the first level&lt;/span&gt;
                    &lt;span class="c1"&gt;// process the next branch or a leaf (i.e. emission factor data)&lt;/span&gt;
                    &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;childIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;branchLevel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// target the specific index of each branch level's children array&lt;/span&gt;
                        &lt;span class="cm"&gt;/** @type { boolean } */&lt;/span&gt;
                        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSearchResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;branchLevel&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;// have we gotten to the last element in the search yet?&lt;/span&gt;
                        &lt;span class="cm"&gt;/** @type { branch } */&lt;/span&gt;
                        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;branchInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nx"&gt;branchInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentBranch&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;branchInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;buildBranchSearchRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentBranch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;childIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;branchLevel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isSearchResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// build the next level of the search route&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isSearchResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// this will also be the end of the current forEach iteration&lt;/span&gt;
                            &lt;span class="c1"&gt;// incrementally build the HTML tree to reflect the search tree routes&lt;/span&gt;
                            &lt;span class="nx"&gt;newLevelOneNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createEfHtmlTree&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;branchInfo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nx"&gt;branchInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                            &lt;span class="nx"&gt;branchInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;collateEfDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentBranch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                            &lt;span class="nx"&gt;currentBranch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;branchInfo&lt;/span&gt; &lt;span class="c1"&gt;// move down the tree in the data&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="cm"&gt;/** @type { HTMLElement } */&lt;/span&gt;
                        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createEfHtmlTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;branchInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nx"&gt;currentNode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="nx"&gt;currentNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nextNode&lt;/span&gt; &lt;span class="c1"&gt;// move down the tree in the (soon-to-be) DOM&lt;/span&gt;
                        &lt;span class="nx"&gt;currentBranch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;branchInfo&lt;/span&gt; &lt;span class="c1"&gt;// move down the tree in the data&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newLevelOneNode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;tree&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I managed to build all this logic and get it working. It felt like I'd bashed out some code to get a job done then move on to the next thing. But it bothered me so much that I kept picking at the logic to try and see how to refactor it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Refactoring
&lt;/h1&gt;

&lt;p&gt;Some steps that got me to what I think is a much better solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Call

&lt;code&gt;buildTree&lt;/code&gt;

in a map, to take out the first level of

&lt;code&gt;forEach&lt;/code&gt;

loops. But I was confounded by the types, as it returns HTMLElements that don't sit well with a regular array. So I decided the data types needed to be dealt with separately - search results and UI.&lt;/li&gt;
&lt;li&gt;Consider the operation to select the successful search results as a filter operation. Here, I considered that the process of keeping the "paths" through each tree's branches and children based on search success/fail is basically a filter. But not a straightforward one as would need to be an iterative/recursive filter down the lists of children.&lt;/li&gt;
&lt;li&gt;In fact, focusing on the search results separately from UI meant that I could build new successful-hits-only search data via a map &lt;em&gt;then&lt;/em&gt; use this to create UI. The clarity provided by separated data and the filter concept led to a better solution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I guess I was finally following some basic data management principles of separating types out and thinking more about functional operations rather than direct UI creation.&lt;/p&gt;

&lt;p&gt;The resulting code is spread over several functions but hopefully provides a much more obvious logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collatedSearches&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchResultsArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collateSearchResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;collatedSearches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buildSearchResultNodes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// will be added to the DOM&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * This function acts as a sort-of filter, only returning data for those child elements that appeared in the successful search "matches"
 * @param { searchResult } searchResult 
 * @return { collatedSearch }
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;collateSearchResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;colName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;colName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;efDetailsList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/** @type { match } */&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;searchResultLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;moveAlongSearchPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;collateEfDetails&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchResultLocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Follow a search path recursively down a branch
 * @param { branch } branch 
 * @param { number[] } path
 * @return { branch } 
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;moveAlongSearchPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;branch&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;moveAlongSearchPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;branch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="cm"&gt;/**
 * Build a simple UI structure from a list of successful searches
 * @param { collatedSearch } collatedSearchResult 
 * @return { HTMLDivElement }
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buildSearchResultNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collatedSearchResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createEfHtmlTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;collatedSearchResults&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// need to create top-level for Class1Name&lt;/span&gt;
    &lt;span class="nx"&gt;collatedSearchResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;efDetailsList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;ef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// add each ef from the list&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createEfHtmlTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// create a checkbox element&lt;/span&gt;
        &lt;span class="nx"&gt;rootElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nextElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rootElement&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The jsDocs types used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** 
 * @typedef { object } searchResult
 * @property { branch } item
 * @property { number } refIndex
 * @property { match[] } matches
 */&lt;/span&gt;
&lt;span class="cm"&gt;/** 
 * @typedef { object } branch
 * @property { branch[] } children
 * @property { string } colName
 * @property { string | efValue } value
 * @property { number } [efCount]
 */&lt;/span&gt;
&lt;span class="cm"&gt;/** 
 * @typedef { object } match
 * @property { number } idx
 * @property { number | undefined } indices
 * @property { pathKey } key
 * @property { number } norm
 * @property { number[] } path
 * @property { number } score
 * @property { string } value
 */&lt;/span&gt;
&lt;span class="cm"&gt;/** 
 * @typedef { object } collatedSearch
 * @property { string } colName
 * @property { string } value
 * @property { efValue[] } efDetailsList
 */&lt;/span&gt;
&lt;span class="cm"&gt;/** 
 * @typedef { object } efValue
 * @property { string } name
 * @property { number | string | boolean } cf
 * @property { string } unit
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What puzzles me still as a learning developer, however, is whether given the same sort of task again I'd now leap straight to the "clean" reasonably functional approach, or still have to start imperatively.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>functional</category>
      <category>refactorit</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Is web security broken?</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Thu, 26 Nov 2020 08:38:38 +0000</pubDate>
      <link>https://dev.to/johnkazer/is-web-security-broken-4l1b</link>
      <guid>https://dev.to/johnkazer/is-web-security-broken-4l1b</guid>
      <description>&lt;p&gt;Or just our approach to it?&lt;/p&gt;

&lt;p&gt;Why does this subject, so fundamental, seem so hard?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The internet, including DEV, is full of articles on security, JWT, cookies-good, cookies-bad etc.&lt;/li&gt;
&lt;li&gt;Some services (e.g. Azure Active Directory) seem to handle it all for you - but do they really? And when can you actually use this approach?&lt;/li&gt;
&lt;li&gt;Pretty much any article on the subject will come with a stream of comment and debate attached - doubt is sown...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's a professional, let alone a beginner, to do?&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>help</category>
    </item>
    <item>
      <title>Functional programming is conversational, OOP is abstract art</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Mon, 28 Sep 2020 08:35:23 +0000</pubDate>
      <link>https://dev.to/johnkazer/functional-programming-is-conversational-oop-is-abstract-art-219h</link>
      <guid>https://dev.to/johnkazer/functional-programming-is-conversational-oop-is-abstract-art-219h</guid>
      <description>&lt;p&gt;Functional, OOP and procedural styles are all valid, just different - and in fact complementary.&lt;/p&gt;

&lt;p&gt;To explore how, here's a fundamental question: What is it &lt;em&gt;really&lt;/em&gt; like to program in a functional, procedural or an OOP style? I don't mean the technical nuts-and-bolts, which many folk have and will write about. I mean from a personal perspective - what thinking style are you using and is it suited to the task at hand?&lt;/p&gt;

&lt;p&gt;I was reading &lt;a href="https://dev.to/aismagulov/a-process-is-the-opposite-of-personal-interaction-35p6"&gt;an article by Arsen&lt;/a&gt; recently, on the different interactions implicit in software engineering teams. For example, engineering processes are basically code procedures that are both impersonal and hard to stop once started. But very useful and efficient for well-understood activities.&lt;/p&gt;

&lt;p&gt;Consider modern art (such as the article's header pic which is a part of "Little painting of fir-trees" by Paul Klee). It takes a lot of thought and consideration to distill a thought or a scene into an abstract representation on canvas. And once done it is hard to undo - the links between painting and reality are not always obvious or reversible. But the end-point is powerful and imparts meaning quickly. Of course, the meaning you extract might not always be what the painter intended you to see!&lt;/p&gt;

&lt;p&gt;Contrast this with the interactive conversation that is pair-programming or an agile sprint. Feedback is rapid, change is easy (desired) and you have more control over the questions and answers.&lt;/p&gt;

&lt;p&gt;A common development process therefore might start with some rapid-feedback activity, when the solution space isn't clear. But over time become more structured.&lt;/p&gt;

&lt;p&gt;We can apply the same concept to the style of programming. Start functional, move to objects with message passing once the structure is clearer and more organisation is needed (the &lt;a href="https://dev.to/stopachka/classes-are-just-a-fancy-way-of-writing-higher-order-functions-7k"&gt;objects may be functions with closures&lt;/a&gt; or some other OOP concept). Then convert some of the fundamental activities into fast and efficient procedures when you can cope with the loss of control this implies.&lt;/p&gt;

&lt;p&gt;Functional, OOP and procedural styles are all valid, just different - and in fact complementary.&lt;/p&gt;

</description>
      <category>functional</category>
      <category>oop</category>
    </item>
    <item>
      <title>Don't be confused by closures, currying etc</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Fri, 11 Sep 2020 22:11:37 +0000</pubDate>
      <link>https://dev.to/johnkazer/don-t-be-confused-by-closures-currying-etc-2ji5</link>
      <guid>https://dev.to/johnkazer/don-t-be-confused-by-closures-currying-etc-2ji5</guid>
      <description>&lt;p&gt;If you are worrying about the how and the detail, just relax. Focus on the logical operations.&lt;/p&gt;

&lt;p&gt;The fundamental issue of a functional style is to deal in the logic of data transforms. You don't need to know how currying works, just use it (e.g. via a library like &lt;a href="https://ramdajs.com/docs/#curry"&gt;Ramda&lt;/a&gt;). Avoid the books and tutorials that try to explain the technical features and give examples of functions implementing partial application - at least for now.&lt;/p&gt;

&lt;p&gt;If you are used to a more imperative style where the how is important, such as the detailed structure of a for loop, then it can be confusing dealing with the details of closures, currying, partial application and such like. There are many resources telling you in detail how these things work. But functional programming is about logic and data manipulation. You don't have to worry about the detail, so don't! Understanding what these tools can do is important, details less so.&lt;/p&gt;

&lt;h1&gt;
  
  
  Write pseudo-code and re-use or write functions to match.
&lt;/h1&gt;

&lt;p&gt;The first time I really felt I "got" the concept of currying wasn't by reading examples explaining how to build a curry or partial application function from scratch. It was just using the concept to solve a simple problem. Now, I won't say that the following problem or solution are good practice but that doesn't matter either :-)&lt;/p&gt;

&lt;p&gt;A simple example in pseudo-code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a basic button so the app is usable&lt;/li&gt;
&lt;li&gt;save a way to update it later&lt;/li&gt;
&lt;li&gt;render the basic button&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;delay here&lt;/strong&gt; whilst getting some info from the user&lt;/li&gt;
&lt;li&gt;eventually get the information to update the button&lt;/li&gt;
&lt;li&gt;use my saved method to make the update&lt;/li&gt;
&lt;li&gt;optionally do this again, with the exact same functions but different data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The key feature here is the delay in getting some data then being able to apply it to make a change&lt;/strong&gt;. A bonus is being able to repeat this again, reliably. When there is a delay, use currying (or possibly a closure) to retain functionality or data for later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;curry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ramda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * return { HTMLButtonElement }
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="cm"&gt;/** @type { HTMLButtonElement } */&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/** @type { string } */&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="cm"&gt;/** @type { HTMLButtonElement } */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttonToUpdate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementsById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;renderBasicButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/** @type { HTMLButtonElement } */&lt;/span&gt; &lt;span class="nx"&gt;buttonToUpdate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dummy label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// create the button with an initial label&lt;/span&gt;
&lt;span class="c1"&gt;// generate a new function&lt;/span&gt;
&lt;span class="nx"&gt;renderButtonWithLateLabel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;updateButton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/** @type { HTMLButtonElement } */&lt;/span&gt; &lt;span class="nx"&gt;buttonToUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// use currying to store the ability to update the button later&lt;/span&gt;
&lt;span class="c1"&gt;// renderButtonWithLateLabel is now a function that has stored the button. It's just waiting for the second argument, the string, to come along later.&lt;/span&gt;

&lt;span class="c1"&gt;// do stuff, get a string from the user maybe&lt;/span&gt;

&lt;span class="cm"&gt;/** @type { string } */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;labelDefinedLater&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;renderButtonWithLateLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/** @type { string } */&lt;/span&gt; &lt;span class="nx"&gt;labelDefinedLater&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// now we complete the logical step by updating the "saved" button with a new label&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;So a toy example but it was all I needed to grasp the principle of currying. It's all about delayed functionality.&lt;/p&gt;

&lt;p&gt;Another more useful example is managing the values in the objects within an array. Say I wanted to update a value 'good' in a specific object (e.g. with a specified value of 'key'), whilst retaining the overall array integrity. This could be &lt;a href="https://stackoverflow.com/questions/4689856/how-to-change-value-of-object-which-is-inside-an-array-using-javascript-or-jquer"&gt;quite a complex process&lt;/a&gt; of mutated data, nested loops, searching through each object, finding the one to change, making the change and rebuilding the array.&lt;/p&gt;

&lt;p&gt;However, I did a google search with a phrase that mirrors the general logic I was after "update value in an object array ramda" and the &lt;a href="https://stackoverflow.com/questions/46628013/change-an-object-property-in-array-with-ramda"&gt;first result&lt;/a&gt; is what I want.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;propEq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assoc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ramda&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;assoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayWithUpdatedObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;22&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// in the array "items", find the object with 'key' is 22 and set 'good' to be true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Which is fine, but is a rather one-shot sort of thing. If I make the first argument to &lt;code&gt;propEq&lt;/code&gt; an argument to the curried function it becomes more interesting. By making an additional argument, we can save an aspect of functionality to use later.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;alterBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propEq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alterBy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;assoc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alterByKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// save the concept of matching the objects by key&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;arrayWithUpdatedObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alterByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;22&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// will alter the object with key === '22'&lt;/span&gt;

&lt;span class="c1"&gt;// alternatively you could create a separate instance which identifies objects to alter by id&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alterById&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;alter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;In many ways a closure is similar to currying. You are keeping some data or functionality around to be used later. The "how" of creating a closure isn't really important - the key thing is it's usefulness in your logical process.&lt;/p&gt;

&lt;p&gt;Here &lt;a href="https://dev.to/stopachka/classes-are-just-a-fancy-way-of-writing-higher-order-functions-7k"&gt;is a great example&lt;/a&gt; of using a closure to store data and functionality for later. A function is created called &lt;code&gt;person&lt;/code&gt; that contains data (&lt;code&gt;firstName&lt;/code&gt;, &lt;code&gt;lastName&lt;/code&gt;) and functions (&lt;code&gt;getFullName&lt;/code&gt;, &lt;code&gt;setFullName&lt;/code&gt;) which are stored for later use.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__296048"&gt;
  
    .ltag__user__id__296048 .follow-action-button {
      background-color: #2e0338 !important;
      color: #ffffff !important;
      border-color: #2e0338 !important;
    }
  
    &lt;a href="/stopachka" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ojGZkPhQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--vPYJCjkt--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/296048/66e69501-f18e-4db4-8ef3-0b898e4b2efe.jpg" alt="stopachka image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/stopachka"&gt;Stopa&lt;/a&gt;
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/stopachka"&gt;&lt;/a&gt;
    &lt;/div&gt;
    &lt;p class="ltag__user__social"&gt;
        &lt;a href="https://twitter.com/stopachka" rel="noopener"&gt;
          &lt;img class="icon-img" alt="twitter logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--oEHrSmvE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-logo.svg"&gt;stopachka
        &lt;/a&gt;
        &lt;a href="https://github.com/stopachka" rel="noopener"&gt;
          &lt;img class="icon-img" alt="github logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--C74Jn3f1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo.svg"&gt;stopachka
        &lt;/a&gt;
    &lt;/p&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>beginners</category>
      <category>functional</category>
    </item>
    <item>
      <title>DEV's UI decision-making - A question...</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Fri, 28 Aug 2020 14:09:01 +0000</pubDate>
      <link>https://dev.to/johnkazer/ui-decision-making-example-42pk</link>
      <guid>https://dev.to/johnkazer/ui-decision-making-example-42pk</guid>
      <description>&lt;p&gt;Hi &lt;/p&gt;
&lt;div class="ltag__user ltag__user__id__1"&gt;
  
    .ltag__user__id__1 .follow-action-button {
      background-color: #3B21E2 !important;
      color: #C9FAF2 !important;
      border-color: #3B21E2 !important;
    }
  
    &lt;a href="/ben" class="ltag__user__link profile-image-link"&gt;
      &lt;div class="ltag__user__pic"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bgwIhvJ3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--1M1qt9Sp--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/1/f451a206-11c8-4e3d-8936-143d0a7e65bb.png" alt="ben image"&gt;
      &lt;/div&gt;
    &lt;/a&gt;
  &lt;div class="ltag__user__content"&gt;
    &lt;h2&gt;
&lt;a class="ltag__user__link" href="/ben"&gt;Ben Halpern&lt;/a&gt;
&lt;/h2&gt;
    &lt;div class="ltag__user__summary"&gt;
      &lt;a class="ltag__user__link" href="/ben"&gt;A Canadian software developer who thinks he’s funny. He/Him.&lt;/a&gt;
    &lt;/div&gt;
    &lt;p class="ltag__user__social"&gt;
        &lt;a href="https://twitter.com/bendhalpern" rel="noopener"&gt;
          &lt;img class="icon-img" alt="twitter logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--oEHrSmvE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/twitter-logo.svg"&gt;bendhalpern
        &lt;/a&gt;
        &lt;a href="https://github.com/benhalpern" rel="noopener"&gt;
          &lt;img class="icon-img" alt="github logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--C74Jn3f1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo.svg"&gt;benhalpern
        &lt;/a&gt;
        &lt;a href="http://benhalpern.com" rel="noopener"&gt;
          &lt;img class="icon-img" alt="external link icon" src="https://res.cloudinary.com/practicaldev/image/fetch/s--WsHTbjfA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/link.svg"&gt;http://benhalpern.com
        &lt;/a&gt;
    &lt;/p&gt;


&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;
 I'm interested in why the new(ish) DEV (desktop) interface invites you to 'Add Comment' when there are no comments yet on a post. Does this suggest you should write a comment without reading the content?

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lGr71SlB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/awm8r6161v31jqk8hdut.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lGr71SlB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/awm8r6161v31jqk8hdut.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An alternative that seems more natural to me would be to simply invite you to read the post...&lt;/p&gt;

&lt;p&gt;What was the thought process in this case?&lt;/p&gt;

&lt;p&gt;The mobile view just tells you there are none:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dcuooDRf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5wzmaocpokt0g05tse55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dcuooDRf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5wzmaocpokt0g05tse55.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ux</category>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Why functional programming? Stay in control</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Fri, 29 Nov 2019 10:33:31 +0000</pubDate>
      <link>https://dev.to/johnkazer/stay-in-control-55m7</link>
      <guid>https://dev.to/johnkazer/stay-in-control-55m7</guid>
      <description>&lt;p&gt;As I delve a bit more into the experience of converting myself into a semi-literate functional programmer, I still wonder what is the point.&lt;/p&gt;

&lt;p&gt;Well, one interesting realisation occurred today and it is about control.&lt;/p&gt;

&lt;p&gt;I was looking at some code, a straightforward JavaScript &lt;code&gt;array.forEach&lt;/code&gt; construct. I thought about what was needed to convert it into something more functional - e.g. a &lt;code&gt;array.map&lt;/code&gt;. What would happen? I'd need a return value to use as the logical effect of the function. What was I doing right now? It was hard to say - need to read the code and find out what the effect really is.&lt;/p&gt;

&lt;p&gt;And then it hit me - using &lt;code&gt;forEach&lt;/code&gt; means I was losing control of the code. It didn't feel 'safe' somehow. If I was asking for a return value then I'd still be in control because then I can do something with it. Using &lt;code&gt;forEach&lt;/code&gt; gives all that responsibility away. I have to trust the called function to do the right thing.&lt;/p&gt;

&lt;p&gt;Maybe this is a consequence of writing functional code and my emotional response to code is changing?&lt;/p&gt;

&lt;p&gt;Hard to say, but it gave me some insight into the psychology of code wars and declarative (functional) vs procedural approaches.&lt;/p&gt;

&lt;p&gt;Conclusion: Functional programmers like control and certainty, procedural programmers are more chilled (or maybe more anxious).&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>functional</category>
    </item>
    <item>
      <title>Functional programming is great, but not for the reasons everyone keeps telling you</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Wed, 13 Nov 2019 14:31:48 +0000</pubDate>
      <link>https://dev.to/johnkazer/functional-programming-is-great-but-not-for-the-reasons-everyone-keeps-telling-you-dnd</link>
      <guid>https://dev.to/johnkazer/functional-programming-is-great-but-not-for-the-reasons-everyone-keeps-telling-you-dnd</guid>
      <description>&lt;p&gt;There is no necessarily fundamental reason why functional programming is better than a procedural or object oriented approach. Yes, pure functions, clean code, readability etc. &lt;em&gt;may&lt;/em&gt; make your programmes safer and easier to reason about.&lt;/p&gt;

&lt;p&gt;But that isn't why I like functional programming.&lt;/p&gt;

&lt;p&gt;I simply prefer the way it allows me to think about problem solving. Maybe the world is inherently split into programmers who think functionally and those who think procedurally - someone should fund a psychology research project!&lt;/p&gt;

&lt;p&gt;As an example, say I have some data structure and would like to transform it. My thought process is to start writing down a list of actions that I think will be needed. This is an iterative process, as the consequences of each action becomes clearer and extra steps are added. I might also realise that a particular action is really a special case of something that already exists - so I just co-opt that action (or function). In doing so, I may have to change it to make it more generic - but that's fine because it's a pure function and therefore safe.&lt;/p&gt;

&lt;p&gt;After a while, I might write some pseudo code which simply sets out the logical action sequence - often the initial list of actions &lt;em&gt;is&lt;/em&gt; the pseudo code. And this pseudo code often ends up being the actual function I want. Then there is a detail which is the additional implementation needed to flesh-out some of the functions implied by specific actions in the pseudo code.&lt;/p&gt;

&lt;p&gt;If there is iteration needed, that's a single recursive function which can be defined in pseudo code by name at this stage. It is sufficient to know that an iteration is needed and what form the output will take.&lt;/p&gt;

&lt;p&gt;If there is a sequence of actions needed, that's a composition which the pseudo code represents with a sequence of functions.&lt;/p&gt;

&lt;p&gt;If the logic determines that data needs transforming (e.g. filtering) then that's fine, just state it as an action. The result will be used further down the pseudo code and no need to worry about side-effects because there are none.&lt;/p&gt;

&lt;p&gt;So the logical steps to get from initial data to result are quickly clear and little additional infrastructure is needed to just make it happen. This to me is how many people already solve problems and therefore functional programming doesn't necessarily need any change of mindset.&lt;/p&gt;

&lt;p&gt;However, the wrinkle is that the incremental state of the data is not immediately obvious - the functional approach almost wants you to not care. And therefore your real mindset switch is to one of imagining data structure transformations as the chain of functions progresses.&lt;/p&gt;

</description>
      <category>functional</category>
      <category>algorithms</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Revist the waterfall process but this time with maths</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Tue, 20 Aug 2019 21:31:43 +0000</pubDate>
      <link>https://dev.to/johnkazer/revist-the-waterfall-process-but-this-time-with-maths-27cn</link>
      <guid>https://dev.to/johnkazer/revist-the-waterfall-process-but-this-time-with-maths-27cn</guid>
      <description>&lt;p&gt;(Cover image by &lt;a href="https://unsplash.com/@johnwestrock?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;John Westrock&lt;/a&gt; on &lt;a href="https://unsplash.com/search/photos/waterfall?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;"Design, maths &amp;amp; framework".&lt;/p&gt;

&lt;p&gt;The "you really should read" &lt;a href="https://rauchg.com/2015/pure-ui"&gt;Pure UI&lt;/a&gt; and &lt;a href="https://medium.com/@asolove/pure-ui-control-ac8d1be97a8d"&gt;Pure UI Control&lt;/a&gt; articles defined an approach to app development (design and creation) which resolves to these 3 words. If their approach to maths is extended slightly to include testing, then we can say these 3 words constitute a comprehensive but simple waterfall development process.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KNsMfCmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/num9dfr6rq80d3bafnp1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KNsMfCmW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/num9dfr6rq80d3bafnp1.png" alt="3-word dev process flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I find this an attractive simplification, in which maths helps developer's lives become easier, less stressful and less process-dependent.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Design&lt;/em&gt; defines the appearance and the core states and transitions. &lt;em&gt;Maths&lt;/em&gt; formalises the states and transitions, whilst visually and collaboratively supporting the investigation of missing details and then auto-generating tests. The app &lt;em&gt;framework&lt;/em&gt; simply instantiates those states and transitions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pure UI design
&lt;/h2&gt;

&lt;p&gt;The essence of 'Pure UI', as eloquently described in the articles at the beginning of this post, is a smooth interaction between design and business logic. UI is really a side-effect of the business logic, or state management. The chosen solution to provide this apparent magic is state charts. It doesn't really matter which framework you use, so long as it's functional and state -&amp;gt; UI is uni-directional!&lt;/p&gt;

&lt;h2&gt;
  
  
  What are state charts and how do they help?
&lt;/h2&gt;

&lt;p&gt;State charts are a well-described mathematical concept (an &lt;a href="https://statecharts.github.io/"&gt;intro&lt;/a&gt; and some &lt;a href="https://statecharts.github.io/resources.html"&gt;resources&lt;/a&gt;) which extend the simpler state machine. The core principles are the definition of states, transitions between states and actions to take in consequence. Optional extras are the management of state and implementation of the actions (I prefer to let the app or UI framework deal with these).&lt;/p&gt;

&lt;p&gt;A state chart allows reasoning about function without concern for implementation details - it is therefore easier. In particular, &lt;a href="https://xstate.js.org/viz/?gist=61fb76fa71bd9216a21c70d1f241da91"&gt;visualisation&lt;/a&gt; of a state chart enables easy collaboration with designers and rapid prototyping (or updating) of function. The linked visualisation is from the app example in 'Implementation options' below, and is a simple copy-paste of a state chart from the &lt;a href="https://github.com/johnkazer/hyperapp-xstate-demo/blob/xstate/src/machines.js"&gt;machines.js&lt;/a&gt; file in that app. A state chart designed in this way can therefore be used directly in code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated testing with state charts
&lt;/h2&gt;

&lt;p&gt;Much, of course, is written about unit tests. Functional programming, a natural fit for state machines and React or Hyperapp means unit tests are reasonably straightforward. What is always less so is testing business logic, or the interactions between functions and state which happen when the UI generates related sequences of events.&lt;/p&gt;

&lt;p&gt;Cypress can automate business logic testing if state chart 'paths' are provided, which represent automatically generated routes or sequences of transitions. XState has a &lt;a href="https://xstate.js.org/docs/packages/xstate-graph/#quick-start"&gt;graph module&lt;/a&gt; which does this.&lt;/p&gt;

&lt;p&gt;There are less than 10 simple app-specific lines of code, amongst re-usable functions, which drive the &lt;a href="https://github.com/johnkazer/hyperapp-xstate-demo/blob/xstate/cypress/integration/tests.js"&gt;Cypress test&lt;/a&gt; of each app state and valid 'path'. You can watch the test video 'live' &lt;a href="https://github.com/johnkazer/hyperapp-xstate-demo/blob/xstate/cypress/videos/tests.js.mp4"&gt;here&lt;/a&gt; and the app is &lt;a href="https://picswithhyperappxstate.z33.web.core.windows.net/"&gt;hosted here&lt;/a&gt; (it is a simple media creation &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps"&gt;PWA&lt;/a&gt;, which doesn't send data anywhere but asks for video, audio and push notification permissions).&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintenance
&lt;/h2&gt;

&lt;p&gt;New requirements simply filter in from the top, via design updates and logical consequences of state chart changes. In principle the tests will automatically update as a function of the state charts (in practice some small amount of manual input may be required).&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation options
&lt;/h2&gt;

&lt;p&gt;There are some details to work out regarding the specific boundary of responsibility between the maths and the app framework (e.g. exactly who hosts the state) but these don't change the principles.&lt;/p&gt;

&lt;p&gt;For instance, in &lt;a href="https://dev.to/johnkazer/use-maths-not-process-for-reliable-web-apps-1bmm"&gt;this example&lt;/a&gt;, the &lt;a href="https://hyperapp.dev/"&gt;Hyperapp&lt;/a&gt; framework is responsible for managing state and executing actions. However in &lt;a href="https://codesandbox.io/s/xstate-todomvc-33wr94qv1?from-embed"&gt;this&lt;/a&gt; example &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; is largely just an interface to the browser's DOM whilst the state chart (from &lt;a href="https://xstate.js.org"&gt;XState&lt;/a&gt;) manages state and action execution. Specifically in this case, DOM events are linked to a &lt;a href="https://xstate.js.org/docs/packages/xstate-react/#api"&gt;React Hook&lt;/a&gt; which triggers a state transition - the state chart does the rest.&lt;/p&gt;

&lt;p&gt;A very useful (but little written about?) state chart feature is that they can automatically provide app use-cases as sequences of state transitions. These sequences, or paths, can be used as the basis for testing - &lt;a href="https://dev.to/johnkazer/use-maths-not-process-for-reliable-web-apps-1bmm"&gt;this example&lt;/a&gt; drives &lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt; in this way. The repository is &lt;a href="https://github.com/johnkazer/hyperapp-xstate-demo"&gt;here&lt;/a&gt; - use the 'xstate' branch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Waterfalls with maths
&lt;/h2&gt;

&lt;p&gt;So there you have it - a waterfall dev process that is much simpler and friendlier than I remember. And some maths that isn't scary and is useful!&lt;/p&gt;

&lt;p&gt;An example development process flow might look something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Creating the initial &lt;em&gt;design&lt;/em&gt; with &lt;a href="https://www.figma.com/"&gt;Figma&lt;/a&gt; and &lt;a href="https://sketch.systems/ryanlucas/sketch/a58505b25ac114c6cec64922cb8fb325"&gt;sketch.systems&lt;/a&gt; (use &lt;a href="https://github.com/johnkazer/Export-Figma-layer-IDs"&gt;this Figma plugin&lt;/a&gt; and see the 'Export to clipboard' option in sketch.systems) which informs, and is informed by;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;State chart &lt;em&gt;maths&lt;/em&gt; in &lt;a href="https://xstate.js.org/"&gt;XState&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UI via a &lt;em&gt;framework&lt;/em&gt; (such as &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; or &lt;a href="https://hyperapp.dev/"&gt;Hyperapp&lt;/a&gt;) is a side-effect of state chart behaviour.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Testing can be derived directly from the state chart &lt;em&gt;maths&lt;/em&gt; and implemented with &lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Requirement changes can quickly be incorporated into &lt;em&gt;design&lt;/em&gt; and state chart &lt;em&gt;maths&lt;/em&gt; updates, which (generally) require simple UI &lt;em&gt;framework&lt;/em&gt; tweaks.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>testing</category>
      <category>javascript</category>
      <category>statemachine</category>
      <category>maths</category>
    </item>
    <item>
      <title>Use maths not process for reliable web apps</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Fri, 16 Aug 2019 18:55:18 +0000</pubDate>
      <link>https://dev.to/johnkazer/use-maths-not-process-for-reliable-web-apps-1bmm</link>
      <guid>https://dev.to/johnkazer/use-maths-not-process-for-reliable-web-apps-1bmm</guid>
      <description>&lt;p&gt;A combination of functional front-end JavaScript and state machines leads to unit and business logic tests which can approach mathematical proof. &lt;a href="https://github.com/johnkazer/hyperapp-xstate-demo/blob/xstate/cypress/videos/tests.js.mp4" rel="noopener noreferrer"&gt;Watch&lt;/a&gt; as Cypress steps through the app, using an auto-generated graph of the state machine that defines the app's possible UI actions and transitions.&lt;/p&gt;

&lt;p&gt;Disclaimer - to follow this post you can probably get away with knowing that Hyperapp manages state immutably, uses a virtual DOM and hosts pure functions to handle actions (such as click handlers), effects such as http requests and subscriptions to external events or streams. XState manages state machine nodes and transitions plus actions triggered by transition. The key to this demo is associating Hyperapp and XState actions. Some familiarity with basic Cypress testing will help too.&lt;/p&gt;

&lt;p&gt;However, it might help to have some knowledge of &lt;a href="https://github.com/jorgebucaran/hyperapp" rel="noopener noreferrer"&gt;Hyperapp&lt;/a&gt; actions and events plus &lt;a href="https://xstate.js.org/docs/" rel="noopener noreferrer"&gt;state machines&lt;/a&gt;, as these are big subjects mostly out of scope of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why state machines?
&lt;/h2&gt;

&lt;p&gt;De-stress app development.&lt;/p&gt;

&lt;p&gt;Not only will you know that your app's &lt;em&gt;pure functions&lt;/em&gt; work (existing unit testing) but you will also know that the &lt;em&gt;business logic&lt;/em&gt; which wires them together also works!&lt;/p&gt;

&lt;p&gt;You can use easily accessible mathematical principles to build reliable apps rather than depending on software process. This article focuses on business logic rather than unit testing.&lt;/p&gt;

&lt;p&gt;Using an XState machine means that you can pre-define and test how sequences of user actions and events lead to state change and therefore app behaviour. The logic is clear and easy to reason about (rather than potentially obscured in collections of functions or separate hard-to-maintain flow diagrams). You can &lt;a href="https://statecharts.github.io/xstate-viz/" rel="noopener noreferrer"&gt;visualise&lt;/a&gt; the logic with interactive state machine charts and create &lt;a href="https://glebbahmutov.com/blog/hyperapp-state-machine/#testing-from-state-machine" rel="noopener noreferrer"&gt;tests&lt;/a&gt; easily. More on tests later but note that the state machine drives UI tests which prove functionality matches the logic.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;More&lt;/em&gt; - if you update the requirements and change the state machine(s) the tests will automatically update as well.&lt;/p&gt;

&lt;h1&gt;
  
  
  How?
&lt;/h1&gt;

&lt;p&gt;A Hyperapp demo&lt;/p&gt;

&lt;p&gt;Here I introduce a &lt;a href="https://hyperapp.dev/" rel="noopener noreferrer"&gt;Hyperapp&lt;/a&gt; demo, which implements a simple multi-media &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps" rel="noopener noreferrer"&gt;PWA&lt;/a&gt; (Progressive Web App) with &lt;a href="https://xstate.js.org" rel="noopener noreferrer"&gt;XState&lt;/a&gt;. You can take a photo, create a recording and simulate uploading them to the cloud. If offline, the PWA will save locally and automatically 'upload' when you're back online. Apologies for the requested browser permissions - video, audio and push notifications - they are necessary for the app's functionality. But nothing leaves your browser!&lt;/p&gt;

&lt;p&gt;The repository is &lt;a href="https://github.com/johnkazer/hyperapp-xstate-demo" rel="noopener noreferrer"&gt;here&lt;/a&gt;. The master branch just uses Hyperapp, whilst the xstate branch modifies the app to use XState state machines as well. Use 'npm start' to build and run using Parcel. There are two state machines, one to control taking and saving a photo, the other to make a sound recording.&lt;/p&gt;

&lt;p&gt;Note that unlike most Hyperapp implementations, this app uses &lt;a href="https://dev.to/johnkazer/hyperapp-with-pug-templates-517e"&gt;Pug to render HTML&lt;/a&gt;. You may prefer to convert to the more usual JSX or hyperscript approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works - Hyperapp
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://hyperapp.dev/" rel="noopener noreferrer"&gt;Hyperapp&lt;/a&gt; maintains a central state and message handler which listens for user actions and browser events. When an action or event changes the state, Hyperapp uses a virtual-DOM to update the app. A loop exists, half visible to the developer and half within the framework.&lt;/p&gt;

&lt;p&gt;Action -&amp;gt; Event -&amp;gt; [ Listener -&amp;gt; State management -&amp;gt; Virtual-DOM ] -&amp;gt; DOM-change -&amp;gt; Action...&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works - XState
&lt;/h2&gt;

&lt;p&gt;State machines are a long-standing mathematical tool. Their practical application to apps has some common practical and conceptual features with how Hyperapp defines app behaviour. The main difference is that &lt;a href="https://xstate.js.org" rel="noopener noreferrer"&gt;XState&lt;/a&gt; enables the relationships between Actions, Events and State to be defined unambiguously in one place.&lt;/p&gt;

&lt;p&gt;A state machine is created in JSON, whilst the XState system provides pure functions for interacting with it. Your app can respond to action and event-driven change from the machine or request state changes directly.&lt;/p&gt;

&lt;p&gt;Action -&amp;gt; [ Transition -&amp;gt; Possible Actions too choose from] -&amp;gt; Action...&lt;/p&gt;

&lt;p&gt;There are two state machines in the demo, one to manage taking photo's and the other for recordings.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://statecharts.github.io/xstate-viz/" rel="noopener noreferrer"&gt;visualize&lt;/a&gt; and interact with the machines defined in machines.js. I have found it useful to compare the experience of visual logic with that of using the actual app or a mock-up. You can focus on the function without being distracted by form.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works - XState within Hyperapp
&lt;/h2&gt;

&lt;p&gt;Two new functions that manage the link. One captures events (e.g. button clicks) and the other converts XState actions into Hyperapp actions. In effect, these functions act as a bridge between Hyperapp actions and state, and XState actions and transitions.&lt;/p&gt;

&lt;p&gt;There are some important features of the app implementation which help this process. In particular, I get easy linking between UI and logic by using the same id names for UI elements as for their associated Hyperapp action function names and XState transitions. Conversely, it is a bit fiddly switching between different state machines - a balance between monolithic vs distributed.&lt;/p&gt;

&lt;p&gt;For the video handling part of the app, each button has this &lt;code&gt;onclick=[updateVideoState, targetId]&lt;/code&gt; tuple as a click handler.&lt;/p&gt;

&lt;p&gt;The click handler function &lt;code&gt;updateVideoState&lt;/code&gt; receives &lt;code&gt;state&lt;/code&gt; and the element's &lt;code&gt;id&lt;/code&gt; from Hyperapp, which handles the DOM event. It passes these and state machine details to &lt;code&gt;processNewState&lt;/code&gt; shown below. This function calls &lt;code&gt;runActions&lt;/code&gt; to link state machine actions associated with the transition to the execution of Hyperapp actions. It must handle the results of Hyperapp actions (new state), events (which return &lt;code&gt;[state, event]&lt;/code&gt; tuples) and one-shot effects (no returned object). Finally &lt;code&gt;processNewState&lt;/code&gt; returns the updated state to Hyperapp, along with the latest state machines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processNewState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;machineState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;machineName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previousState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pathOr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;machineState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;machineState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newMachineState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;previousState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 'id' is synonymous with 'newState'&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;runActions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newMachineState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;machineName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;STATE_MACHINES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VIDEO_MACHINE&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;newMachineState&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoState&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;audioState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;machineName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;STATE_MACHINES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUDIO_MACHINE&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;newMachineState&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audioState&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// capture the result of an action&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;videoState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;audioState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// capture the result of an action-effect tuple&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;videoState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;audioState&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;videoState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;audioState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// state machine was updated&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;runActions&lt;/code&gt; function executes the action defined by the machine and allows Hyperapp to process the result. The actions are all regular Hyperapp action functions, the only difference from regular use of Hyperapp is that the state machine determines what action to execute when a valid transition occurs. Note that an action in XState is maintained as an array of &lt;code&gt;action.exec&lt;/code&gt; functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runActions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;calcState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;evtObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// make recursive or map&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="nx"&gt;calcState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stateChangeRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;evtObj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stateChangeRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;requests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isArray&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;stateChangeRequest&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;stateChangeRequest&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;requests&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Testing!
&lt;/h1&gt;

&lt;p&gt;There are two parts to the testing issue - unit testing of pure functions and testing the app logic generated by sets of functions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Unit testing Pure functions
&lt;/h2&gt;

&lt;p&gt;Written about by many others...&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing app logic with state machines
&lt;/h2&gt;

&lt;p&gt;Self-writing tests! What's not to love?&lt;/p&gt;

&lt;p&gt;Well, not completely self-writing but not far off. XState provides a &lt;a href="https://xstate.js.org/docs/packages/xstate-graph/" rel="noopener noreferrer"&gt;graph feature&lt;/a&gt; which generates a set of objects and arrays which describe the paths through your app's state machines. I've implemented an approach using &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; where control of the app tests is managed by these paths. See cypress/integration/tests/tests.js in the xstate branch of the repo.&lt;/p&gt;

&lt;p&gt;Here's an example of the Cypress output, showing execution of a 3-stage path to reach 'upload'. Notice again the shared naming between DOM element id and transition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1otsi69ksgdv8y9l2vzi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1otsi69ksgdv8y9l2vzi.png" alt="Cypress test example following an XState path"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the example, there are four states and actions defined in the machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoMachine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Machine &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videoMachine&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;initial&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videoState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;states&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;videoState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;captureImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;capture&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;captureImage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;capture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;captured&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videoState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;captured&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;uploadImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uploadImage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;discardImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videoState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discardImage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;uploadSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videoState&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="na"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discardImage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="p"&gt;},&lt;/span&gt;
                &lt;span class="na"&gt;uploadFail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;captured&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow through the Cypress results alongside the state machine. Here's a summary, with the transition 'actions' in []&lt;/p&gt;

&lt;p&gt;videoState -&amp;gt; [&lt;em&gt;captureImage&lt;/em&gt;] -&amp;gt; capture -&amp;gt; [&lt;em&gt;success&lt;/em&gt;] -&amp;gt; captured -&amp;gt; [&lt;em&gt;uploadImage&lt;/em&gt;] -&amp;gt; upload -&amp;gt; [&lt;em&gt;uploadSuccess&lt;/em&gt;] -&amp;gt; videoState&lt;/p&gt;

&lt;p&gt;The first transition &lt;code&gt;captureImage&lt;/code&gt; takes a picture and displays the result in a &lt;code&gt;div&lt;/code&gt;. If this is successful, the second transition doesn't involve an action as Hyperapp resolves the result of an Effect in the background. &lt;code&gt;success&lt;/code&gt; of the Effect allows the display of new buttons - to upload or discard. The path gives us an &lt;code&gt;uploadImage&lt;/code&gt; action followed by the final step where Hyperapp manages the result of the 'uploadSuccess' effect in the background.&lt;/p&gt;

&lt;p&gt;The outcome of the final 'uploadSuccess' step is not immediately obvious, but Cypress 'time-travel' (scrolling back through each test step) shows that we requested upload and received a success response afterwards.&lt;/p&gt;

&lt;p&gt;Make the upload request (button click):&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F15h33d6gl7n6ccv28owq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F15h33d6gl7n6ccv28owq.png" alt="Click the upload button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Uploaded and ready to take the next picture:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fk4hzr3lgzo68bllw67ac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fk4hzr3lgzo68bllw67ac.png" alt="Successful upload"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the core of the test code for Cypress (I've not show the XState-graph objects as they're complex, but are essentially lists of transitions).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkTransition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`checking transition from &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; due to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// if there are paths[x].state.actions[y] then have Hyperapp and XState run the action(s) for us&lt;/span&gt;
    &lt;span class="c1"&gt;// Optionally we could drive the machine from here too&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;machineName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;currentMachine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// machine.transition(...)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// else run paths[x].event&lt;/span&gt;
    &lt;span class="c1"&gt;// add logic to check for DOM elements with id === paths[x].event&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`button[id="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listOfPaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;curry&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;selectPaths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`List of paths state: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;selectPaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkTransition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// the test&lt;/span&gt;
&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tests each state path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:1234&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;listOfVideoPaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOfPaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectSimpleVideoPaths&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testVideo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appIsReady&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;checkPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;listOfVideoPaths&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test video paths&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;testVideo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoStateList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As such, the core code is pretty reusable for any XState-graph, the only custom tweaking needed is selection of the actual UI action, &lt;code&gt;.click()&lt;/code&gt; in this case.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Other aspects of the app may be suitable for state machines, including upload/http, online/offline state and PWA installation.&lt;/p&gt;

&lt;p&gt;There is an interesting boundary of responsibility between Hyperapp and XState that this demo has only just started to explore - such as state management and action definition.&lt;/p&gt;

&lt;p&gt;It would be useful to build a system for defining the relationship between DOM elements being acted upon and the desired action (e.g. click, hover etc.). This is rather manual at present.&lt;/p&gt;

&lt;p&gt;Also check out &lt;a href="https://rosmaro.js.org/" rel="noopener noreferrer"&gt;Rosmaro&lt;/a&gt; for a state machine implementation - it has a handy graphical UI for creating machines rather than hand-coding JSON.&lt;/p&gt;

&lt;p&gt;What role components?&lt;/p&gt;

&lt;p&gt;How many state machines do you need and how big should they be?&lt;/p&gt;

&lt;p&gt;XState/graph returns a subset of possible paths but not all, so it would be a useful upgrade to ensure full coverage.&lt;/p&gt;

&lt;p&gt;Is it a scalable concept?&lt;/p&gt;

&lt;p&gt;Please add comments!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>functional</category>
      <category>testing</category>
      <category>statemachine</category>
    </item>
    <item>
      <title>Hyperapp with Pug templates</title>
      <dc:creator>John Kazer</dc:creator>
      <pubDate>Fri, 28 Jun 2019 09:19:24 +0000</pubDate>
      <link>https://dev.to/johnkazer/hyperapp-with-pug-templates-517e</link>
      <guid>https://dev.to/johnkazer/hyperapp-with-pug-templates-517e</guid>
      <description>&lt;p&gt;I recently completed an &lt;a href="https://www.udemy.com/functional-programming-for-beginners-with-javascript/"&gt;excellent course&lt;/a&gt; by James Moore on the basics of functional JavaScript for web apps. The app framework he created is nice but uses &lt;a href="https://github.com/hyperhype/hyperscript"&gt;hyperscript&lt;/a&gt; to define HTML as functions. I find hyperscript to be an interesting approach and it does allow for composable UI components. But I don't really get on with it...&lt;/p&gt;

&lt;p&gt;As I was browsing DEV, I noticed &lt;a href="https://dev.to/aspittel/functional-programming-in-javascript-done-right-with-hyperapp-570f"&gt;a post&lt;/a&gt; from &lt;a class="comment-mentioned-user" href="https://dev.to/aspittel"&gt;@aspittel&lt;/a&gt;
 which mentioned &lt;a href="https://github.com/jorgebucaran/hyperapp"&gt;Hyperapp&lt;/a&gt;. Turns out this is built along very similar lines with an option to use JSX as well as hyperscript. I imagine the new v2 has moved on quite a bit from when Ali checked it out in early 2018.&lt;/p&gt;

&lt;p&gt;Now, the Hyperapp framework is wonderful and has a number of features I like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clear functional approach to business logic&lt;/li&gt;
&lt;li&gt;State-driven UI&lt;/li&gt;
&lt;li&gt;Centralised state and no stateful components (easy 'undo' option and perfect for quick and reliable development of small to medium scale apps)&lt;/li&gt;
&lt;li&gt;Events dispatched to update the state which updates the UI using virtualDOM diff&lt;/li&gt;
&lt;li&gt;Fast, small and simple but sufficient&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, previously I've used &lt;a href="https://pugjs.org/api/getting-started.html"&gt;Pug&lt;/a&gt; to define my UI templates. I like the retained ability to see the page structure and the clearer separation of logic and UI. Combining HTML with the business logic using hyperscript h functions doesn't sit right with me (yet?) and I find it hard to reason about a page when the layout is so abstract.&lt;/p&gt;

&lt;p&gt;Maybe I'll come round eventually, but maybe I don't need to...&lt;/p&gt;

&lt;p&gt;Fortunately for me, the project &lt;a href="https://github.com/batiste/pug-vdom"&gt;pug-vdom&lt;/a&gt; (obviously) brings a virtualDOM to Pug. So what follows is a brief intro to the &lt;em&gt;very&lt;/em&gt; simple app I built to demo how Hyperapp can use Pug templates. See the &lt;a href="https://github.com/jorgebucaran/hyperapp"&gt;Hyperapp pages&lt;/a&gt; to get a better understanding of the full range of what it does, or try their new &lt;a href="https://www.udemy.com/hands-on-web-development-with-hyperapp-v2/learn/lecture/14983948"&gt;Udemy&lt;/a&gt;/&lt;a href="https://www.packtpub.com/gb/application-development/hands-web-development-hyperapp-v2-video"&gt;Packt&lt;/a&gt; course.&lt;/p&gt;

&lt;p&gt;As project setup, here's the package.json. Key items to note being the start script and pug/pug-vdom dependencies (and you need &lt;a href="https://nodejs.org/en/"&gt;Node.js&lt;/a&gt; version 6.4 &lt;a href="https://node.green/"&gt;or above&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hyperapp-pug"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An instance of hyperapp which uses pug and pug-vdom rather than the default h functions"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"main"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"main.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node ./compilePug.js &amp;amp;&amp;amp; parcel ./src/index.html"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Kazer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ISC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"parcel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.12.4"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hyperapp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0.12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pug"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0.4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pug-vdom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.1.2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is the basic project file structure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;\dist (parcel output dir)
\src
  app.pug.js (compiled pug template)
  index.html
  main.js
  pug-to-view.js
  pug-vdom.js (added as a local copy to handle Hyperapp approach to textNodes)
\views
  app.pug
compilePug.js (see the start script)
package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note:&lt;/em&gt; There's &lt;a href="https://github.com/batiste/pug-vdom/pull/52"&gt;a PR for pug-vdom&lt;/a&gt; to include this tweak, so perhaps in future the local copy can go away.&lt;/p&gt;

&lt;p&gt;As you can imagine, after running "npm install", using the "npm run start" script runs a compilation process which converts the Pug view into a .js file. The compilation is defined by compilePug.js. The compiled file is included &lt;code&gt;require('./app.pug.js')&lt;/code&gt; by main.js and provides the virtual DOM nodes that Hyperapp needs to display the content. Then &lt;a href="https://parceljs.org/"&gt;parcel&lt;/a&gt; runs, updates the other files in \src and starts a browser instance of \dist\index.html.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!doctype html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Hyperapp demo&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;'app'&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./main.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compile process is pretty simple - a list of the templates and destinations (in this case just the one):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// compilePug.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vDom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pug-vdom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;vDom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./views/app.pug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/app.pug.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./views&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It compiles my simple Pug template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Need a root div to grab as the start of the view
div
    // receives the variables and functions from the supplied state object
    - var greeting = "Hello " + greet
    p(style={color: "red"}) #{greeting}
    input(size="60" placeholder=placeholder onchange=handler.updateMe)
    button(id='clickMe' onclick=handler.clickMe) Click Me
    p #{userText}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's have a quick look at the main.js which defines the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hyperapp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pugToView&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./pug-to-view&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pugToView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// event handlers&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;clickMe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateMe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;friends&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Write something here first, hit &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;enter&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;&amp;gt; then click the button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;clickMe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;updateMe&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where the helper function pugToView comes from here&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pug-vdom/runtime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// runtime library is required and puts 'pugVDOMRuntime' into the global scope&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.pug.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pugToView&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;text&lt;/span&gt;
  &lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// grabs the root 'div' element whilst adjusting the way pug-vdom deals with props compared to hyperapp&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main.js file uses the three API functions provided by Hyperapp (yes, just three!), &lt;code&gt;h&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt; and &lt;code&gt;app&lt;/code&gt;. These functions provide state management, virtual DOM, diffing and DOM patching when state changes.&lt;/p&gt;

&lt;p&gt;This is a static page, so there is only a simple initial state; some initial values and the event handlers to associate with the button and input elements.&lt;/p&gt;

&lt;p&gt;We are going to inject the app at the selected &lt;code&gt;node&lt;/code&gt; by providing the &lt;code&gt;view&lt;/code&gt; as defined by the compiled Pug template using the &lt;code&gt;content&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;app&lt;/code&gt; function pulls all this together and Hyperapp takes care of the rest. Simple declarative, functional, logic coupled to a familiar templating system!&lt;/p&gt;

&lt;p&gt;Find the repo &lt;a href="https://github.com/johnkazer/hyperapp-pug"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>virtualdom</category>
      <category>pug</category>
      <category>functional</category>
    </item>
  </channel>
</rss>
