<?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: Margarita Krutikova</title>
    <description>The latest articles on DEV Community by Margarita Krutikova (@margaretkrutikova).</description>
    <link>https://dev.to/margaretkrutikova</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%2F134269%2F2ba2a0d0-34fb-4601-a8d2-5c8791d24db5.JPG</url>
      <title>DEV Community: Margarita Krutikova</title>
      <link>https://dev.to/margaretkrutikova</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/margaretkrutikova"/>
    <language>en</language>
    <item>
      <title>Set up Event Store on AWS with CDK</title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Sun, 27 Feb 2022 18:44:31 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/set-up-event-store-on-aws-with-cdk-42bi</link>
      <guid>https://dev.to/margaretkrutikova/set-up-event-store-on-aws-with-cdk-42bi</guid>
      <description>&lt;p&gt;&lt;a href="https://www.eventstore.com/"&gt;Event store&lt;/a&gt; can be deployed as a managed instance via &lt;a href="https://www.eventstore.com/event-store-cloud"&gt;Event Store Cloud&lt;/a&gt; which will take care of availability, clustering and other candies. The only drawback is the high &lt;a href="https://www.eventstore.com/event-store-cloud/pricing"&gt;cost&lt;/a&gt; of the hosting resources plus a separate cost (also high IMO 😛) for &lt;a href="https://www.eventstore.com/support"&gt;support&lt;/a&gt;. Here we won't take any fancy shortcuts with managed clouds and do it on our own in &lt;code&gt;AWS&lt;/code&gt; using infrastructure as code with &lt;a href="https://aws.amazon.com/cdk/"&gt;&lt;code&gt;CDK&lt;/code&gt;&lt;/a&gt; in &lt;code&gt;Typescript&lt;/code&gt;. We will have one event store cluster running in ECS &lt;code&gt;Fargate&lt;/code&gt; service accessing an external file system, or &lt;a href="https://aws.amazon.com/efs/"&gt;EFS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;CDK&lt;/code&gt; code is available on &lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk"&gt;&lt;code&gt;github&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Quick links to content:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup overview&lt;/li&gt;
&lt;li&gt;CDK best practices&lt;/li&gt;
&lt;li&gt;
CDK Infrastructure

&lt;ul&gt;
&lt;li&gt;Stateful resources&lt;/li&gt;
&lt;li&gt;Event store&lt;/li&gt;
&lt;li&gt;Creating stacks and dependencies&lt;/li&gt;
&lt;li&gt;Enable access to event store&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Scientific diagram of how everything hangs together&lt;/li&gt;
&lt;li&gt;Access EventStore Admin UI&lt;/li&gt;
&lt;li&gt;Bonus: generating certificate&lt;/li&gt;
&lt;li&gt;Next steps&lt;/li&gt;
&lt;li&gt;Acknowledgments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup overview
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;CDK&lt;/code&gt; code will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a &lt;a href="https://aws.amazon.com/vpc/"&gt;VPC&lt;/a&gt; (Virtual Private Cloud) where the file system will reside,&lt;/li&gt;
&lt;li&gt;create a file system for persisting events,&lt;/li&gt;
&lt;li&gt;spin up a container running &lt;code&gt;EventStoreDB&lt;/code&gt; on ECS &lt;a href=""&gt;Fargate&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;configure security group access to the event store from another &lt;code&gt;Fargate&lt;/code&gt; service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CDK best practices
&lt;/h2&gt;

&lt;p&gt;A couple of very important &lt;code&gt;CDK&lt;/code&gt; &lt;a href="https://aws.amazon.com/blogs/devops/best-practices-for-developing-cloud-applications-with-aws-cdk/"&gt;best practices&lt;/a&gt; that we will follow in our setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;organize app in logical units that extend &lt;code&gt;Construct&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;group constructs into deployment units that extend &lt;code&gt;Stack&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;separate stacks with stateful and stateless resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following these will greatly help when your app grows - you will need to refactor or add more resources. For the above setup we will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Constructs:

&lt;ul&gt;
&lt;li&gt;VPC where we will attach the file system and ECS cluster for event store,&lt;/li&gt;
&lt;li&gt;file system,&lt;/li&gt;
&lt;li&gt;ECS cluster and &lt;code&gt;Fargate&lt;/code&gt; service for running event store,&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Stacks:

&lt;ul&gt;
&lt;li&gt;a stateful stack with the VPC and EFS constructs,&lt;/li&gt;
&lt;li&gt;a stateless stack with the event store construct.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  CDK Infrastructure
&lt;/h2&gt;

&lt;p&gt;EFS is a stateful resource and it depends on VPC (removing it will nuke the EFS), so it makes sense to group these two resources in one stack. The rest - ECS cluster and event store service - belong to a separate stack that can be destroyed and re-deployed without any data loss.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stateful resources
&lt;/h3&gt;

&lt;p&gt;Here is how the &lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk/blob/master/lib/constructs/vpc.ts"&gt;vpc&lt;/a&gt; and &lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk/blob/master/lib/constructs/file-system.ts"&gt;file system&lt;/a&gt; look, one thing to note is &lt;code&gt;enableAutomaticBackups&lt;/code&gt; when creating the file system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;efs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FileSystem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;AppFileSystem&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;vpc&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;vpc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;removalPolicy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RemovalPolicy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RETAIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;enableAutomaticBackups&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can configure backups manually or turn on automatic backups (&lt;code&gt;enableAutomaticBackups&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt;). However you need to be aware of the default setting of automatic backups. According to the &lt;a href="https://docs.aws.amazon.com/aws-backup/latest/devguide/create-auto-backup.html"&gt;docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The default backup window (the time frame when the backup will run) is set to start at 5 AM UTC (Coordinated Universal Time) and lasts 8 hours.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And to ensure consistent backups AWS &lt;a href="https://docs.aws.amazon.com/efs/latest/ug/alternative-efs-backup.html"&gt;recommends&lt;/a&gt; pausing any writes to the file system or unmounting it during the backup.&lt;/p&gt;

&lt;p&gt;If you don't want to keep these default settings, you shouldn't enable automatic backups and instead configure your own backup vault and backup rules, e.g. here is a backup rule to run at 2AM UTC within 2 hours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;backupVault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BackupVault&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;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;backup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BackupPlan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FileSystemBackupPlan`, {
    backupVault,
    backupPlanRules: [
      new backup.BackupPlanRule({
        completionWindow: Duration.hours(2),
        startWindow: Duration.hours(1),
        scheduleExpression: events.Schedule.cron({ hour: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, minute: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; }),
        deleteAfter: Duration.days(35),
      }),
    ],
});
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you enable automatic backups and change your mind later, the automatic backup vault can't be removed. It will just be there, cold and lonely, until the end of times.&lt;/p&gt;

&lt;p&gt;Now the stateful resources and their dependencies (VPC and file system) are packaged into a deployment unit &lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk/blob/master/lib/stateful-stack.ts"&gt;&lt;code&gt;StatefulStack&lt;/code&gt;&lt;/a&gt;. From this stack we expose the VPC and file system as two publicly accessible properties so that our event store construct can use them later when constructing the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;StatefulStack&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Stack&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FileSystem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IVpc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="kr"&gt;string&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;StackProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystem&lt;/span&gt; &lt;span class="o"&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;h3&gt;
  
  
  Event store
&lt;/h3&gt;

&lt;p&gt;This beast is tricky, here is what we do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create an ECS cluster to run a &lt;code&gt;Fargate&lt;/code&gt; service,&lt;/li&gt;
&lt;li&gt;create volumes for event store logs and data in the file system,&lt;/li&gt;
&lt;li&gt;create a &lt;code&gt;Fargate&lt;/code&gt; task definition and attack the volumes,&lt;/li&gt;
&lt;li&gt;create an event store container and mount the volumes,&lt;/li&gt;
&lt;li&gt;create a &lt;code&gt;Fargate&lt;/code&gt; service and connect it with the task definition,&lt;/li&gt;
&lt;li&gt;give the &lt;code&gt;Fargate&lt;/code&gt; service access to the file system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk/blob/master/lib/constructs/event-store.ts"&gt;Here&lt;/a&gt; is the full code for the event store construct. It needs access to the file system and the VPC and we can pass them as props when creating an instance of the construct.&lt;/p&gt;

&lt;p&gt;Let's assume we have created a cluster and a &lt;code&gt;Fargate&lt;/code&gt; task definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cluster&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Cluster&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;taskDefiniton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FargateTaskDefinition&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next we create volumes and attach them to the task definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;esVolumeLogs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Volume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eventstore-volume-logs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;efsVolumeConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fileSystemId&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;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystemId&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;esVolumeData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Volume&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eventstore-volume-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;efsVolumeConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fileSystemId&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;fileSystem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystemId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;transitEncryption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ENABLED&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="nx"&gt;taskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addVolume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;esVolumeLogs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;taskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addVolume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;esVolumeData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When creating an event store we are pulling an &lt;a href="https://hub.docker.com/r/eventstore/eventstore"&gt;eventstore&lt;/a&gt; image from docker hub, specifying two external ports for &lt;code&gt;TCP&lt;/code&gt; (1113) and &lt;code&gt;HTTP&lt;/code&gt; (2113) communication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;esContainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;taskDefinition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addContainer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EventStoreContainer&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;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContainerImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eventstore/eventstore:21.10.0-buster-slim&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;containerName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EventStore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;portMappings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2113&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1113&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;EVENTSTORE_CLUSTER_SIZE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;EVENTSTORE_EXT_TCP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1113&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;EVENTSTORE_HTTP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2113&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;EVENTSTORE_ENABLE_EXTERNAL_TCP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;EVENTSTORE_INSECURE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;EventStore&lt;/code&gt; image supports built-in self-signed or real certificate, see event store &lt;a href="https://developers.eventstore.com/server/v5/security.html#security"&gt;security&lt;/a&gt;. Setting &lt;code&gt;EVENTSTORE_INSECURE&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; will allow us to skip this for now, we will deal with it later.&lt;/p&gt;

&lt;p&gt;And now we can mount the volumes we created earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;esContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addMountPoints&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;containerPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/var/log/eventstore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sourceVolume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;esVolumeLogs&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="na"&gt;readOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;esContainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addMountPoints&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;containerPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/var/lib/eventstore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sourceVolume&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;esVolumeData&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="na"&gt;readOnly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last, we create a &lt;code&gt;Fargate&lt;/code&gt; service on the cluster and with the task definition defined above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating stacks and dependencies
&lt;/h3&gt;

&lt;p&gt;In our &lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk/blob/master/bin/app.ts"&gt;&lt;code&gt;bin/app.ts&lt;/code&gt;&lt;/a&gt; we assemble the stacks and constructs taking into account their dependencies. An instance of &lt;code&gt;StatefulStack&lt;/code&gt; is created first, the event store stack is created on the fly inside the construct and accepts resources from &lt;code&gt;StatefulStack&lt;/code&gt; as dependencies in &lt;code&gt;props&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;cdk&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statefulResources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;StatefulStack&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;StatefulResources&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;eventStoreResources&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventStoreConstruct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Stack&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;EventStore&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="s2"&gt;EventStore&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;fileSystem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;statefulResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileSystem&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;statefulResources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&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;h3&gt;
  
  
  Enable access to event store
&lt;/h3&gt;

&lt;p&gt;Assume we have another &lt;code&gt;Fargate&lt;/code&gt; service and we want to give it access to our event store. We need to add inbound rules in the event store security group to allow access on specific ports from the security group of this new service.&lt;/p&gt;

&lt;p&gt;Ideally we don't want to configure security group rules inside the event store construct/stack because it will invert the dependency arrow - it is not the event store that depends on that external service, but the external service depends on the event store. In this way the deployment cycle of the event store is not affected by deployments of the dependent stacks.&lt;/p&gt;

&lt;p&gt;One solution is to allow other &lt;code&gt;CDK&lt;/code&gt; constructs to configure access to the event store security group that we can expose from the event store construct. The plan is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;expose &lt;code&gt;connections&lt;/code&gt; object from the &lt;code&gt;Fargate&lt;/code&gt; service on the construct,&lt;/li&gt;
&lt;li&gt;pass the &lt;code&gt;connections&lt;/code&gt; as a dependency in &lt;code&gt;props&lt;/code&gt; in the other service construct,&lt;/li&gt;
&lt;li&gt;configure access to the service security group via &lt;code&gt;allowTo&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Changes we want to make inside the event store construct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;EventStoreConstruct&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Connections&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="kr"&gt;string&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;EventStoreProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&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;fargateService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FargateService&lt;/span&gt;&lt;span class="p"&gt;(..);&lt;/span&gt;

    &lt;span class="c1"&gt;// a lot of code&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connections&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fargateService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connections&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;And our service construct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;OtherServiceProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IVpc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;eventStoreConnections&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IConnectable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;OtherServiceConstruct&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;Construct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Construct&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="kr"&gt;string&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;OtherServiceProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// setup cluster, fargate service, task definition etc.&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fargateService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FargateService&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;

    &lt;span class="nx"&gt;fargateService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowTo&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;eventStoreConnections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2113&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nx"&gt;fargateService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connections&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allowTo&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;eventStoreConnections&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tcp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1113&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;Our &lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk/blob/master/bin/app.ts#L22"&gt;&lt;code&gt;bin/app.ts&lt;/code&gt;&lt;/a&gt; can now include the new service and accept event store connections as dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scientific diagram of how everything hangs together
&lt;/h2&gt;

&lt;p&gt;Here is a highly scientific graph of the AWS resources and connections between them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LHj34Gog--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6l3uzedzegodyexu27p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LHj34Gog--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6l3uzedzegodyexu27p4.png" alt="AWS resources and connections between them" width="880" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Access EventStore Admin UI
&lt;/h2&gt;

&lt;p&gt;You can enable access to &lt;code&gt;EventStore&lt;/code&gt; &lt;a href="https://developers.eventstore.com/server/v5/admin-ui.html#dashboard"&gt;Admin UI&lt;/a&gt; for testing purposes by exposing port &lt;code&gt;2113&lt;/code&gt; to your IP in the inbound rules of the security group that belongs to the &lt;code&gt;Fargate&lt;/code&gt; service we created above. This is not pretty, but hej, we are having fun here ... You should be able to access the Admin UI at &lt;code&gt;http://&amp;lt;FARGATE_TASK_IP&amp;gt;:2113&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I guess it goes without saying that you should remove your IP from the rules and set up a more robust solution (external &lt;code&gt;DNS&lt;/code&gt;, service discovery via &lt;code&gt;AWS Cloud Map&lt;/code&gt; etc) when going live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: generating certificate
&lt;/h2&gt;

&lt;p&gt;To secure communication to/from event store we can generate a certificate with the &lt;a href="https://developers.eventstore.com/server/v20.10/security.html#certificate-generation-tool"&gt;certification generation tool&lt;/a&gt; that comes with event store. There is a docker image and a CLI that will generate a certificate authority and a node certificate for &lt;code&gt;EventStoreDB&lt;/code&gt;, located in a separate &lt;code&gt;github&lt;/code&gt; repo &lt;a href="https://github.com/EventStore/es-gencert-cli"&gt;&lt;code&gt;es-gencert-cli&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This task is not for the faint-hearted, the complete code can be looked at/copied entirely at your discretion &lt;a href="https://github.com/MargaretKrutikova/event-store-aws-cdk/blob/master/lib/constructs/event-store-cert.ts"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The certificate won't be stored in the file system but in the &lt;code&gt;Fargate&lt;/code&gt; task storage and will be re-generated every time the task is updated. Here are the steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create a bind mount host volume and attack it to the task definition,&lt;/li&gt;
&lt;li&gt;add container with &lt;code&gt;es-gencert-cli&lt;/code&gt; image and specify command that will generate a certificate and place it in task storage,&lt;/li&gt;
&lt;li&gt;add mount point to the container,&lt;/li&gt;
&lt;li&gt;add the certificate generator container to the dependencies in the main event store container.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The communication to/from the event store is now encrypted with a self-signed certificate. A more production-ready setup would be nice, but hej, it is just an experiment for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Things to do before going live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add logging and alerts to the event store container,&lt;/li&gt;
&lt;li&gt;change the default password and save it in AWS SecretsManager so that event store clients can safely access it,&lt;/li&gt;
&lt;li&gt;add cloud map service discovery to the event store &lt;code&gt;Fargate&lt;/code&gt; service.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgments
&lt;/h2&gt;

&lt;p&gt;I had the chance to carry out this cool experiment while working at a wonderful place, called &lt;a href="https://insurello.se/blogg/#tech"&gt;Insurello&lt;/a&gt; in Stockholm, Sweden, with the help of my brilliant colleagues. They do tons of functional programming with &lt;code&gt;F#&lt;/code&gt; and &lt;code&gt;Elm&lt;/code&gt;, event sourcing, AWS with &lt;code&gt;CDK&lt;/code&gt; and much more (I am not employed there right now and I miss them).&lt;/p&gt;

</description>
      <category>infrastructure</category>
      <category>aws</category>
      <category>eventstore</category>
      <category>cdk</category>
    </item>
    <item>
      <title>Why IaC and high-level overview of terraform </title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Tue, 14 Sep 2021 14:25:22 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/why-iac-and-high-level-overview-of-terraform-4die</link>
      <guid>https://dev.to/margaretkrutikova/why-iac-and-high-level-overview-of-terraform-4die</guid>
      <description>&lt;p&gt;Infrastructure as code, or &lt;strong&gt;IaC&lt;/strong&gt;, is becoming more mature and there are many tools that help describe infrastructure in code, such &lt;a href="https://www.terraform.io/"&gt;&lt;code&gt;terraform&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://www.pulumi.com/"&gt;&lt;code&gt;pulumi&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://compositionalit.github.io/farmer/"&gt;&lt;code&gt;farmer&lt;/code&gt;&lt;/a&gt; (&lt;code&gt;Azure&lt;/code&gt; only) etc. Let's see why we should jump on this IaC bandwagon and understand its main ideas and benefits, and also learn how terraform implements them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is infrastructure?
&lt;/h2&gt;

&lt;p&gt;It is where your app and its individual parts live - virtual machines, networks, load balancers, db servers, message queues etc. With cloud providers like &lt;code&gt;Azure&lt;/code&gt;, you might have specific resources such as web app, cosmos db, storage account etc.&lt;/p&gt;

&lt;p&gt;A common way of managing infrastructure is manual intervention via some GUI (all of us have at least once logged in to a production server to make a "small" change 😅), writing custom configuration tools to implement a specific setup etc. All of this can become error-prone: no one knows why a change was made or why stuff just stopped working. This can lead to hours of troubleshooting on the production servers.. fun stuff 😱&lt;/p&gt;

&lt;p&gt;With modern cloud providers, manual configuration would look like this: you open your favourite browser, enter &lt;a href="https://portal.azure.com/"&gt;&lt;code&gt;https://portal.azure.com/&lt;/code&gt;&lt;/a&gt; and click through the UI to create a virtual machine:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JxUmSkWz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvfbtwaij76ux5ilcwqp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JxUmSkWz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvfbtwaij76ux5ilcwqp.png" alt="Azure portal UI"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what does IaC do to mitigate these issues?&lt;/p&gt;

&lt;h2&gt;
  
  
  IaC goals
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reproducible infrastructure. Dev, stage, prod environments are consistent, easily reproduced and tested. Stop logging into your server and tweaking some settings or installing some software - you won't be able to reproduce it later and won't know why stuff broke.&lt;/li&gt;
&lt;li&gt;Manage infrastructure state in a &lt;strong&gt;declarative&lt;/strong&gt; way. If you need to make changes to you environment, you don't need to think about &lt;strong&gt;how&lt;/strong&gt; to achieve it or what's already present in the environment. The imperative way is: if &lt;strong&gt;A&lt;/strong&gt; is not installed run command &lt;strong&gt;B&lt;/strong&gt;, then run command &lt;strong&gt;C&lt;/strong&gt; to apply setting &lt;strong&gt;D&lt;/strong&gt; to &lt;strong&gt;A&lt;/strong&gt;. The declarative way is: I want to have &lt;strong&gt;A&lt;/strong&gt; with setting &lt;strong&gt;C&lt;/strong&gt; in my environment, make it happen.&lt;/li&gt;
&lt;li&gt;Increase automation. Apply infrastructure changes as part of the deployment process. Minimize manual human labour (and thus decrease the number of mistakes made).&lt;/li&gt;
&lt;li&gt;Keep infrastructure configuration version-controlled to make changes easier, trace back changes made overtime, collaborate with others and all the other fun stuff one can do with &lt;code&gt;git&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Terraform
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://www.hashicorp.com/products/terraform"&gt;Terraform&lt;/a&gt; allows representing infrastructure as code. Or kinda code ... not like &lt;code&gt;F#&lt;/code&gt; or &lt;code&gt;C#&lt;/code&gt;, but more like &lt;em&gt;declarative configuration files&lt;/em&gt;, like &lt;code&gt;json&lt;/code&gt; or &lt;code&gt;yml&lt;/code&gt; but with some support for functions, modules, etc. The language is called &lt;code&gt;HCL&lt;/code&gt; - &lt;a href="https://www.terraform.io/docs/language/index.html"&gt;HashiCorp Configuration Language&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Keeping with the &lt;em&gt;declarative paradigm&lt;/em&gt; of IaC, the idea is that you tell &lt;code&gt;terraform&lt;/code&gt; what the desired state of the infrastructure is and it will make it happen.&lt;/p&gt;

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

&lt;p&gt;You install &lt;code&gt;terraform&lt;/code&gt; on your machine (fun fact - it is written in &lt;code&gt;Go&lt;/code&gt;) and run &lt;code&gt;terraform&lt;/code&gt; commands in a terminal.&lt;/p&gt;

&lt;p&gt;You tell &lt;code&gt;terraform&lt;/code&gt; what you want your environment to look like by writing your configuration in &lt;code&gt;.tf&lt;/code&gt; files in some folder on your machine. The real state of the environment that corresponds to the configuration is tracked in a terraform state file and acts as a &lt;strong&gt;source of truth&lt;/strong&gt;. When making changes to the configuration, &lt;code&gt;terraform&lt;/code&gt; will use the current state file to know what changes to make in the environment so that it matches the new configuration.&lt;/p&gt;

&lt;p&gt;Do not bypass &lt;code&gt;terraform&lt;/code&gt; by changing your environment via GUI or elsewhere, &lt;code&gt;terraform&lt;/code&gt; state file won't match the actual configuration!&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform provider
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Terraform&lt;/code&gt; can work with different cloud platforms - &lt;code&gt;Azure&lt;/code&gt;, &lt;code&gt;Amazon&lt;/code&gt;, &lt;code&gt;Digital Ocean&lt;/code&gt;, etc. Code you write will differ depending on the cloud platform, since they usually have their own resources and rules specific for that platform.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Terraform&lt;/code&gt; itself is agnostic of the underlying platform, but for managing remote objects on that platform it needs to know how to interact with it - in other words how to call the platform's API to create/modify/delete resources. This is done via terraform plugins called providers. You will need to install a specific cloud provider locally before creating your infrastructure with terraform.&lt;/p&gt;

&lt;p&gt;Here is the sequence of actions terraform takes to make changes to the remote environment:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XSTDDUi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tb7at2mxev8skqisayxk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XSTDDUi0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tb7at2mxev8skqisayxk.png" alt="picture 4"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Terraform commands
&lt;/h2&gt;

&lt;p&gt;You run &lt;code&gt;terraform&lt;/code&gt; commands in a terminal to apply changes to the configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;plan&lt;/code&gt; - reads current state, compares to the configuration, creates a plan of changes (preview of the actions),&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apply&lt;/code&gt; - executed the action proposed in the plan on remote objects,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;destroy&lt;/code&gt; - delete all remote infrastructure for the configuration.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Terraform supports many other exciting features, like remote backends, workspaces, modules etc, but that's for another time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;You want to use infrastructure as code to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;track changes to the infrastructure in &lt;code&gt;git&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;share and collaborate with others,&lt;/li&gt;
&lt;li&gt;revert changes easily if something goes boom 💥,&lt;/li&gt;
&lt;li&gt;make changes easily,&lt;/li&gt;
&lt;li&gt;recreate the same environment easily many times,&lt;/li&gt;
&lt;li&gt;automate changes to the infrastructure during deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terraform implements IaC and stores a file with the state of your remote environment, keeps track of the differences between the configuration and the current state and synchronises the state file with the environment after changes have been made.&lt;/p&gt;




&lt;p&gt;Also published at &lt;a href="https://margareta.dev/blog/infrastructure-as-code-terraform/"&gt;margareta.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>infrastructure</category>
      <category>terraform</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Modelling domain with state machines in ReasonML</title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Sun, 03 May 2020 08:54:01 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/modelling-domain-with-state-machines-in-reasonml-n29</link>
      <guid>https://dev.to/margaretkrutikova/modelling-domain-with-state-machines-in-reasonml-n29</guid>
      <description>&lt;p&gt;Today we are going to focus on modelling state and state handling logic of a system in a functional way, without touching the user interface. &lt;/p&gt;

&lt;p&gt;The idea behind "functional way" here is applying functional patterns and constructs in order to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;model the domain area correctly making impossible states unrepresentable,&lt;/li&gt;
&lt;li&gt;handle domain logic eliminating impossible transitions on the state,&lt;/li&gt;
&lt;li&gt;create a convenient interface for manipulating state that can be plugged in into any frontend (but not necessarily) application,&lt;/li&gt;
&lt;li&gt;write readable and succinct code (less code - fewer bugs 🐛).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The code examples are written in &lt;code&gt;ReasonML&lt;/code&gt;, but will look very similar in other functional languages such as &lt;code&gt;F#&lt;/code&gt; or &lt;code&gt;Elm&lt;/code&gt;. Some familiarity with functional concepts in general will help along the way, but not required.&lt;/p&gt;

&lt;p&gt;Important concepts that we will use extensively:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Immutability and pure functions,&lt;/li&gt;
&lt;li&gt;Variants and pattern matching,&lt;/li&gt;
&lt;li&gt;Partial application and currying.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Just wanted to emphasise that we won't mutate anything here, since it might lead to unexpected bugs, systems that are difficult to follow and debug, and unexpected bugs. Did I mention unexpected bugs?&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Let's imagine that we are building a time tracking system, like an upgraded to-do list, where the user can keep track of how much time it takes to finish tasks. Such a system can be used as a personal tool in private projects or a time reporting tool for software developers at work.&lt;/p&gt;

&lt;p&gt;We will create a module for handling state of this system, gradually introducing more concepts along the way. Our best functional friends in this article will be variants (or discriminated union in &lt;code&gt;F#&lt;/code&gt; and custom types in &lt;code&gt;Elm&lt;/code&gt;) and pattern matching, which we will use to build a state machine.&lt;/p&gt;

&lt;p&gt;The source code is available on &lt;a href="https://github.com/MargaretKrutikova/practical-reason-react/tree/master/modelling-state"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modelling task
&lt;/h2&gt;

&lt;p&gt;According to the requirements of the system, we should be able to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;add tasks,&lt;/li&gt;
&lt;li&gt;start added and pause running tasks,&lt;/li&gt;
&lt;li&gt;mark running or paused tasks as done,&lt;/li&gt;
&lt;li&gt;keep track of how much time a task has been run.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So apart from having a &lt;code&gt;name&lt;/code&gt; and probably &lt;code&gt;id&lt;/code&gt;, a task should know whether it is running, paused, done etc., which we will call &lt;code&gt;status&lt;/code&gt;. Then our &lt;code&gt;task&lt;/code&gt; could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;???,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The status can only be assigned one of the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;not started (just added),&lt;/li&gt;
&lt;li&gt;running with how much time it has been running,&lt;/li&gt;
&lt;li&gt;paused with how much time it ran before it was paused,&lt;/li&gt;
&lt;li&gt;finished with how much time it took in total.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moreover, there are special rules that define whether a task can change its status to a particular value. For example, it wouldn't make sense to pause a task that is done, or start an already running tasks, however it is perfectly fine to set paused or running tasks as done. And the best way to model these rules is using a state machine, which is...&lt;/p&gt;

&lt;h2&gt;
  
  
  State machine
&lt;/h2&gt;

&lt;p&gt;A mathematical model defined by a certain (limited!) number of states, where it can only be in one of these states at a time and can transition between the states based on the current state and some input.&lt;/p&gt;

&lt;p&gt;My first reaction to the definition of a state machine was similar to the one of a "monad" 😨 But let's not allow it happen to you, reader! Simply put, it is a thing that has a limited number of predefined values, the thing can't change its value arbitrary, in order to choose the next value it needs to know the current value and some external input (like a user action).&lt;/p&gt;

&lt;p&gt;State transitions in a state machine can be represented as a function:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;nextState&lt;/code&gt; = &lt;code&gt;transition (currentState, input)&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will model our &lt;code&gt;status&lt;/code&gt; property as a state machine and represent its possible states with a &lt;code&gt;ReasonML&lt;/code&gt; variant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;taskStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotStarted&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each state except &lt;code&gt;NotStarted&lt;/code&gt;, will hold a time interval which we will represent as &lt;code&gt;float&lt;/code&gt; with an alias &lt;code&gt;elapsed&lt;/code&gt; (just for clarity). Those are the possible states of our little state machine. Now, we can define valid state transitions as a following table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NotStarted -&amp;gt; Running&lt;/li&gt;
&lt;li&gt;Running -&amp;gt; Paused&lt;/li&gt;
&lt;li&gt;Running -&amp;gt; Done&lt;/li&gt;
&lt;li&gt;Paused -&amp;gt; Running&lt;/li&gt;
&lt;li&gt;Paused -&amp;gt; Done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will add one more transition from &lt;code&gt;Running&lt;/code&gt; to &lt;code&gt;Running&lt;/code&gt; with elapsed time increased after each timer tick. The input that the state machine will accept can be represented as a variant too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Pause&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Resume&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;Tick&lt;/code&gt; with elapsed time will come from a timer running somewhere in the app, while all the other input values will trigger state transition from the user input (e.g. click on the "Pause" button in the UI). &lt;/p&gt;

&lt;p&gt;Before we implement our transition function, let's look into how we can visualise our state machine with an online tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  State machine visualiser
&lt;/h2&gt;

&lt;p&gt;A great way to illustrate the states and transitions of a state machine is to visualise them with a state diagram. There is an open source visualiser as part of the &lt;a href="https://github.com/davidkpiano/xstate"&gt;&lt;code&gt;xstate&lt;/code&gt;&lt;/a&gt; library, which is an interactive tool where you can click on state transitions and see how state changes.&lt;/p&gt;

&lt;p&gt;Check the task-status example live &lt;a href="https://xstate.js.org/viz/?gist=731a6bcc9291fe38e7edabdca3ce43ef"&gt;here&lt;/a&gt;, and here is a little preview:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GnMjP6HH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/MargaretKrutikova/practical-reason-react/modelling-state/modelling-state/task-status-visualizer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GnMjP6HH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://raw.githubusercontent.com/MargaretKrutikova/practical-reason-react/modelling-state/modelling-state/task-status-visualizer.png" alt="xstate visualizer" width="837" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;br&gt;
&lt;a href="https://github.com/davidkpiano/xstate"&gt;&lt;code&gt;xstate&lt;/code&gt;&lt;/a&gt; is an incredibly powerful library for implementing state machines in &lt;code&gt;Typescript/Javascript&lt;/code&gt; projects with bindings to &lt;code&gt;React&lt;/code&gt; and &lt;code&gt;Vue&lt;/code&gt;.&lt;br&gt;
&lt;/small&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Transition function
&lt;/h2&gt;

&lt;p&gt;The transition function itself will accept a value of type input and the current state, and do a pattern match on both values matching only on valid combinations of pairs &lt;code&gt;(state, input)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotStarted&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pause&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Resume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;+.&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last case will handle all invalid transitions by returning the current state.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: I have been pointed out that when handling default cases with a wildcard match (&lt;code&gt;_&lt;/code&gt;), it is easy to miss potentially valid transitions. Moreover, when making changes and adding states or inputs, the compiler won't give any warning if not handling all combinations explicitly. And I agree!😅 &lt;/p&gt;

&lt;p&gt;However, in more complex systems with many states and inputs, it becomes unreasonable to account for all combinations, since the number of &lt;code&gt;case&lt;/code&gt;s is &lt;code&gt;state * inputs&lt;/code&gt;. But let's see how we can do it for our little state machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotStarted&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Pause&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Resume&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Pause&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt; &lt;span class="o"&gt;+.&lt;/span&gt; &lt;span class="n"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Resume&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Resume&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Pause&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="n"&gt;state&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A little bit more verbose, but still manageable.&lt;/p&gt;




&lt;p&gt;Notice how we encode the business rules of the system in the transition function by allowing to move both running and paused tasks to done. If those rules have to change in the future (and they certainly will), there is only one place where we have to make adjustment and it is quite easy to modify the necessary transitions. &lt;/p&gt;

&lt;p&gt;Speaking of making changes to the system and making sure nothing breaks, it is probably time to test our state machine and fortunately it is quite easy to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;We will use &lt;a href="https://github.com/glennsl/bs-jest"&gt;&lt;code&gt;bs-jest&lt;/code&gt;&lt;/a&gt; for writing tests in &lt;code&gt;ReasonML&lt;/code&gt; and specifically the &lt;code&gt;testAll&lt;/code&gt; function to generate tests based on lists of data. &lt;/p&gt;

&lt;p&gt;Since we probably don't want to write a test per each valid and invalid transition (that would make for a total of 20 tests), we will use &lt;code&gt;testAll&lt;/code&gt; for just two tests - valid and invalid state transitions - to achieve 100% coverage.&lt;/p&gt;

&lt;p&gt;Testing valid transitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;  &lt;span class="n"&gt;testAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"Transition works correctly for valid state transitions"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotStarted&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pause&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Resume&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;currentState&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nextState&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentState&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nextState&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;Here we are feeding a tuple of three values into each test: currentState, input and the expected next state.&lt;/p&gt;

&lt;p&gt;Testing invalid transitions is very similar. We will pass a tuple of a list of states and an input, where each of the combination (state, input) is invalid and should result in the same state after applying the transition function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;  &lt;span class="n"&gt;testAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"Transition returns current state for invalid state transitions"&lt;/span&gt;&lt;span class="o"&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;|&lt;/span&gt;&lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;NotStarted&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Pause&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;NotStarted&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Running&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Resume&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;NotStarted&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Finish&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nc"&gt;NotStarted&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Paused&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Done&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;states&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;State machines help model domain area correctly making sure invalid states and transitions are simply impossible. Moreover, with tools like &lt;code&gt;xstate&lt;/code&gt; visualiser we can discover hidden states or edge cases in a complex system by visualising it with a state diagram.&lt;/p&gt;

&lt;p&gt;State machines are especially useful in frontend development, since a rich app will typically have a fair amount of state that changes based on the user actions. Since the user will probably not use your app as you would expect, eliminating invalid state transitions when the user clicks on the wrong button will spare your app a few crashes 💥&lt;/p&gt;

&lt;p&gt;And the truth is, once you understand a state machine, you will see it everywhere (you are welcome!). However a language that supports variants (or discriminated unions) will truly uncover the power and beauty of state machines.&lt;/p&gt;

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

&lt;p&gt;In the next article I will show how to manipulate a list of tasks in order to implement extra business requirements while writing nice, compact and readable code using partial application and reducer pattern.&lt;br&gt;
Coming 🔜&lt;/p&gt;

</description>
      <category>functional</category>
      <category>reason</category>
      <category>state</category>
      <category>statemachine</category>
    </item>
    <item>
      <title>ReasonReact context explained in action </title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Sun, 22 Sep 2019 15:54:29 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/reason-react-context-explained-in-action-5eki</link>
      <guid>https://dev.to/margaretkrutikova/reason-react-context-explained-in-action-5eki</guid>
      <description>&lt;p&gt;&lt;a href="https://reactjs.org/docs/context.html"&gt;&lt;code&gt;Context&lt;/code&gt;&lt;/a&gt; in &lt;code&gt;react&lt;/code&gt; is designed for sharing some global data between components located at different levels of the component tree. It allows to avoid passing &lt;code&gt;props&lt;/code&gt; all the way down to those components ("prop-drilling") while still updating them whenever the value in &lt;code&gt;context&lt;/code&gt; changes.&lt;/p&gt;

&lt;p&gt;Worth noting that it is recommended to use &lt;code&gt;context&lt;/code&gt; for low-frequent updates (&lt;a href="https://github.com/facebook/react/issues/14110#issuecomment-448074060"&gt;quote by Sebastian Markbåge&lt;/a&gt;), due to a possible performance impact because of to the way &lt;code&gt;react&lt;/code&gt; finds subscribers to the context value. This topic would require its own article (or perhaps a book?), and I will not touch upon it here and instead focus on a practical example of using &lt;code&gt;context&lt;/code&gt; for seldom updates in a &lt;code&gt;react&lt;/code&gt; application with &lt;code&gt;ReasonML&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are we building
&lt;/h2&gt;

&lt;p&gt;We are going to build a feature with log in/log out, where we will put information about the user in &lt;code&gt;context&lt;/code&gt;, so that we can access it from anywhere in our app and customise it depending on whether the user is browsing anonymously or not. The source code in the article is in &lt;a href="https://github.com/MargaretKrutikova/practical-reason-react/tree/master/context"&gt;this repo&lt;/a&gt;, and a link to the mini-app with this feature is &lt;a href="https://practical-reason-react.netlify.com/context/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are many bits and pieces that have to be wired up together in order to get benefits and all the convenience react context provides, especially in a strongly-typed environment with &lt;code&gt;ReasonML&lt;/code&gt;, but it is definitely worth.&lt;/p&gt;

&lt;p&gt;I will go through the steps needed to connect everything together and we will end up with a simple hook that allows to read the user data from context and dispatch and action to update it from any component, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useUser&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;handleLogIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserLoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Anonymous&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/** display login form */&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;LoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/** say hi to the user! */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scroll down to learn how 👇&lt;/p&gt;

&lt;h2&gt;
  
  
  Create provider and context
&lt;/h2&gt;

&lt;p&gt;We will start with these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create context,&lt;/li&gt;
&lt;li&gt;Create provider component,&lt;/li&gt;
&lt;li&gt;Create reusable hook to access context value.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We need to know whether the user using our app is anonymous or logged in, and what actions can change this, so let's start with a few types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** Types.re */&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Anonymous&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;LoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;userAction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;UserLoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;UserLoggedOut&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;LoggedIn&lt;/code&gt; will hold user name, but can be any other type with more user data. We will use &lt;code&gt;userAction&lt;/code&gt; when implementing a reducer for our user state.&lt;/p&gt;

&lt;p&gt;Now let's create context and reusable hook to access the context value, in a file &lt;code&gt;UserContext.re&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** initial value is Anonymous */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Anonymous&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/** hook to easily access context value */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;useUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is very similar to how you would do it in JS. Now let's create context provider in a file &lt;code&gt;UserProvider.re&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** UserProvider.re */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/** Tell bucklescript how to translate props into JS */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;makeProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="bp"&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="s2"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"children"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is that &lt;code&gt;makeProps&lt;/code&gt; for and why can't we just create a normal component with &lt;code&gt;[@react.component]&lt;/code&gt; and &lt;code&gt;make&lt;/code&gt;? The question I asked myself many times until I got tired and dug into it and found out 🤦‍♀️🙃&lt;/p&gt;

&lt;p&gt;Remember how we always have named arguments for &lt;code&gt;props&lt;/code&gt; in our &lt;code&gt;reason&lt;/code&gt; components, like &lt;code&gt;~id&lt;/code&gt; or &lt;code&gt;~className&lt;/code&gt;? JS doesn't have such a feature, and all regular JS components just want to have &lt;code&gt;props&lt;/code&gt; as an object. So how does it compile to valid &lt;code&gt;react&lt;/code&gt; components in JS?&lt;/p&gt;

&lt;p&gt;That's what the attribute &lt;a href="https://reasonml.github.io/reason-react/docs/en/components#reactcomponent"&gt;&lt;code&gt;[@react.component]&lt;/code&gt;&lt;/a&gt; is for. It will generate a function called &lt;code&gt;makeProps&lt;/code&gt;, that transforms those named arguments into a JS object to be used as &lt;code&gt;props&lt;/code&gt; in the JS compiled component.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;React.Context.provider&lt;/code&gt; already generates a react component, that uses &lt;code&gt;props&lt;/code&gt; as a JS object, but we want to use it as a &lt;code&gt;reason&lt;/code&gt; component with named args. That's why we create &lt;code&gt;makeProps&lt;/code&gt; by hand and it will tell bucklescript how to translate our named args into a JS object, consumed as &lt;code&gt;props&lt;/code&gt; by the JS component. And in order to create an object that will compile cleanly to a JS object, we use bucklescript &lt;a href="https://bucklescript.github.io/docs/en/object-2"&gt;&lt;code&gt;Object 2&lt;/code&gt;&lt;/a&gt; bindings, that look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"value"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"children"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we are basically doing the job of &lt;code&gt;[@react.component]&lt;/code&gt;, but luckily it is not much, since the provider just needs a value and children 😅.&lt;/p&gt;

&lt;p&gt;We can now use our provider component like &lt;code&gt;&amp;lt;UserProvider...&amp;gt;&lt;/code&gt; since we followed the &lt;a href="https://reasonml.github.io/reason-react/docs/en/components#component-naming"&gt;&lt;code&gt;convention&lt;/code&gt;&lt;/a&gt; of having two functions &lt;code&gt;make&lt;/code&gt; and &lt;code&gt;makeProps&lt;/code&gt; in a file &lt;code&gt;UserProvider&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update value in context
&lt;/h2&gt;

&lt;p&gt;Now, we want to use our &lt;code&gt;Provider&lt;/code&gt; component and give it the user info, that we can update when the user logs in or logs out.&lt;/p&gt;

&lt;p&gt;The important thing to understand here, is that if we want to &lt;strong&gt;update&lt;/strong&gt; the value in &lt;code&gt;context&lt;/code&gt; and &lt;strong&gt;propagate&lt;/strong&gt; the update to subscriber-components, the value needs to be on the state of some component. This component needs to render the provider component with the value from its own state.&lt;/p&gt;

&lt;p&gt;Let's call it &lt;code&gt;Root&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** Root.re */&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/** user and userAction defined in Types.re */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;UserLoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&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="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;UserLoggedOut&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Anonymous&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;react&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&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;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reducer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Anonymous&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserProvider&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;UserProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cool, now whenever the value in context changes, components using &lt;code&gt;useUser&lt;/code&gt; will be updated with the new value! Wait, the value actually never changes.. oh no! 😯&lt;/p&gt;

&lt;p&gt;Let's give our components possibility to update user data via context. We could pass the update function down as &lt;code&gt;props&lt;/code&gt;, which will be back to prop-drilling approach, but a more fun way is to include &lt;code&gt;dispatch&lt;/code&gt; in the context value itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pass dispatch in context
&lt;/h2&gt;

&lt;p&gt;Let's pass our &lt;code&gt;dispatch&lt;/code&gt; along with &lt;code&gt;user&lt;/code&gt; as context value. Knowing that &lt;code&gt;dispatch&lt;/code&gt; accepts &lt;code&gt;userAction&lt;/code&gt; and returns &lt;code&gt;unit&lt;/code&gt;, we can modify the type of context value in &lt;code&gt;UserContext.re&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** UserContext.re */&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userAction&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;contextValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initValue&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contextValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Anonymous&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ignore&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="cm"&gt;/** no changes when creating context */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and the root component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** Root.re */&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&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;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reducer&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Anonymous&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserProvider&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Page&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;UserProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use context value via hook
&lt;/h2&gt;

&lt;p&gt;And now the reward I promised in the beginning, an easy to use and convenient hook. I will just repeat it here once more, because it is cool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight reasonml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useUser&lt;/span&gt;&lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;handleLogIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserLoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Anonymous&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/** display login form */&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;LoggedIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/** say hi to the user! */&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: optimization techniques
&lt;/h2&gt;

&lt;p&gt;Updating context value will cause subscribed components to re-render. In some cases we might want to avoid extra re-renders if we know they won't bring any updates to our UI. For example, if a component only needs to update user via &lt;code&gt;dispatch&lt;/code&gt;, it won't be interested in any updates to the actual user data, but it will still re-render if the user is updated.&lt;/p&gt;

&lt;p&gt;This can be solved by having the &lt;code&gt;dispatch&lt;/code&gt; function in a separate context, which won't update, since &lt;code&gt;dispatch&lt;/code&gt; is guaranteed to be stable. The other context will have the user data and will update the components that rely on it.&lt;/p&gt;

&lt;p&gt;When the &lt;code&gt;Root&lt;/code&gt; component itself updates (if its &lt;code&gt;props&lt;/code&gt; are updated for example), it will recreate the tuple &lt;code&gt;(user, dispatch)&lt;/code&gt; passed in context and cause subscribed components to update. This can be solved by using &lt;code&gt;useMemo&lt;/code&gt; around the context value to make it stable.&lt;/p&gt;




&lt;p&gt;We have now set up all we need to use context for storing and updating a small amount of global data in our &lt;code&gt;reason-react&lt;/code&gt; application. We have also looked into some underlying mechanisms of how &lt;code&gt;context&lt;/code&gt; works in &lt;code&gt;react&lt;/code&gt; and how components are compiled in &lt;code&gt;reason-react&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Have I missed something or made a mistake? Please let me know in the comments. Or just drop a line about how you are using &lt;code&gt;context&lt;/code&gt; in your application! 💬&lt;/p&gt;

</description>
      <category>reason</category>
      <category>react</category>
      <category>hooks</category>
      <category>context</category>
    </item>
    <item>
      <title>Keyboard accessible dropdown in Elm</title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Fri, 14 Jun 2019 23:59:30 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/keyboard-accessible-dropdown-in-elm-5gh5</link>
      <guid>https://dev.to/margaretkrutikova/keyboard-accessible-dropdown-in-elm-5gh5</guid>
      <description>&lt;p&gt;&lt;sup&gt;&lt;em&gt;- and how I discovered that browsers are awesome!&lt;/em&gt; 😍&lt;/sup&gt;&lt;br&gt;
This is a follow-up on my previous post, where I showed how to &lt;a href="https://dev.to/margaretkrutikova/elm-dom-node-decoder-to-detect-click-outside-3ioh"&gt;decode DOM nodes&lt;/a&gt; from the event object to detect click outside. I am going to use this trick and a couple of more to create a dropdown that can be used with only keyboard.&lt;/p&gt;

&lt;p&gt;I will explain how to properly handle keyboard and focus events, which will serve a good basis for an accessible dropdown. However, I will not cover another important aspect of accessibility - ARIA attributes. The final version will look like this:&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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2t62n1spttq2jhybsn1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2t62n1spttq2jhybsn1.gif" alt="final version"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All source code is on my &lt;a href="https://github.com/MargaretKrutikova/elm-accessible-dropdown" rel="noopener noreferrer"&gt;github&lt;/a&gt; and also on &lt;a href="https://ellie-app.com/5PvmxZtNYw9a1" rel="noopener noreferrer"&gt;ellie&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Let's get formal and define what functionality we want to have. The user should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tab into the dropdown,&lt;/li&gt;
&lt;li&gt;open it with Enter or Space keys,&lt;/li&gt;
&lt;li&gt;focus options while navigating with arrow keys ⬆️ and ⬇️,&lt;/li&gt;
&lt;li&gt;select currently focused option with Enter/Space,&lt;/li&gt;
&lt;li&gt;close the dropdown with Escape or by tabbing out of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;subscribe to &lt;code&gt;keydown&lt;/code&gt; event,&lt;/li&gt;
&lt;li&gt;decode the key that was pressed,&lt;/li&gt;
&lt;li&gt;update the model according to the pressed key,&lt;/li&gt;
&lt;li&gt;set focus on option when navigating with arrow keys,&lt;/li&gt;
&lt;li&gt;handle focus in/out to open/close the dropdown.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  HTML structure
&lt;/h2&gt;

&lt;p&gt;Let's throw in some &lt;code&gt;HTML&lt;/code&gt; for our open dropdown to better understand its structure:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&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;"dropdown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dropdown-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Select option&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dropdown-list"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"option_1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Option 1&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"option_2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Option 2&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
      ...
    &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here we want to attach a custom event listener for &lt;code&gt;keydown&lt;/code&gt; to the root &lt;code&gt;div&lt;/code&gt; (with id &lt;code&gt;dropdown&lt;/code&gt;), and depending on the pressed key, update the model according to the requirements we defined above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model
&lt;/h2&gt;

&lt;p&gt;In order to navigate the options list with up/down arrows, we need to keep track of the currently focused item. We also need to know whether &lt;br&gt;
the dropdown is open, selected option id, and a list of options to show:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;alias&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;selectedId&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;
&lt;h2&gt;
  
  
  Attach custom key event
&lt;/h2&gt;

&lt;p&gt;Elm allows to attach custom event listeners and decode properties from the event object. &lt;code&gt;Html.Events&lt;/code&gt; exposes &lt;a href="https://package.elm-lang.org/packages/elm/html/latest/Html-Events#on" rel="noopener noreferrer"&gt;&lt;code&gt;on&lt;/code&gt;&lt;/a&gt;, which takes in the event name and a &lt;code&gt;JSON&lt;/code&gt; decoder.&lt;/p&gt;

&lt;p&gt;However, if we just listen to &lt;code&gt;keydown&lt;/code&gt; with &lt;code&gt;on&lt;/code&gt; and try pressing up/down arrows, we will see that the whole page scrolls, since it is the default browser behaviour (you can check it yourself in this little &lt;a href="https://ellie-app.com/5PvvT7pXfwBa1" rel="noopener noreferrer"&gt;ellie&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;We can fix this by using &lt;a href="https://package.elm-lang.org/packages/elm/html/latest/Html-Events#preventDefaultOn" rel="noopener noreferrer"&gt;&lt;code&gt;preventDefaultOn&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;on&lt;/code&gt; when attaching the event listener. &lt;code&gt;preventDefaultOn&lt;/code&gt; needs a decoder of tuple for message and a boolean value, that indicates whether to prevent default. Let's use it in the view together with the message for &lt;code&gt;keydown&lt;/code&gt; event:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Events&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Events&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;KeyPressed&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Up&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Down&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Escape&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Enter&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Space&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;Other&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;KeyPress&lt;/span&gt; &lt;span class="kt"&gt;KeyPressed&lt;/span&gt;

&lt;span class="n"&gt;viewDropdown&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;viewDropdown&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;div&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dropdown"&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preventDefaultOn&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keydown"&lt;/span&gt; &lt;span class="n"&gt;keyDecoder&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let's implement &lt;code&gt;keyDecoder&lt;/code&gt; that will receive the event object, decode it, dispatch &lt;code&gt;KeyPress&lt;/code&gt; and prevent default scrolling behaviour. In order to extract the pressed key we can use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key" rel="noopener noreferrer"&gt;&lt;code&gt;event.key&lt;/code&gt;&lt;/a&gt; from the event object and convert it to our custom type &lt;code&gt;KeyPressed&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="n"&gt;keyDecoder&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;keyDecoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key"&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;toKeyPressed&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;KeyPress&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preventDefault&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;preventDefault&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;Up&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;Down&lt;/span&gt;

&lt;span class="n"&gt;toKeyPressed&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;KeyPressed&lt;/span&gt;
&lt;span class="n"&gt;toKeyPressed&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowUp"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Up&lt;/span&gt;

        &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowDown"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Down&lt;/span&gt;

        &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Escape"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Escape&lt;/span&gt;

        &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter"&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Enter&lt;/span&gt;

        &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Space&lt;/span&gt;

        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Other&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: this is not the original formatting of the elm formatter, some extra line breaks were removed to save space on the screen.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;A similar approach of handling keyboard events is documented on &lt;a href="https://github.com/elm/browser/blob/master/notes/keyboard.md" rel="noopener noreferrer"&gt;elm keyboard notes&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update function
&lt;/h2&gt;

&lt;p&gt;In the update function we can handle &lt;code&gt;KeyPress&lt;/code&gt; and react to a specific set of keys depending if the dropdown is currently closed or open. Let's see how the implementation looks in the open state:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="n"&gt;handleKeyWhenOpen&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;KeyPressed&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;handleKeyWhenOpen&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Enter&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="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;selectedId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;Space&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="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;selectedId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;Up&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="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getPrevId&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;Down&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="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getNextId&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;Escape&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="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;Other&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;getPrevId&lt;/code&gt; and &lt;code&gt;getNextId&lt;/code&gt; find the item to the left and to the right from the focused item. You can check their implementation on my &lt;a href="https://github.com/MargaretKrutikova/elm-accessible-dropdown/blob/master/src/AccessibleDropdown.elm#L408" rel="noopener noreferrer"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's check what we have so far:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5wymko7xs6pmrugfh58.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5wymko7xs6pmrugfh58.gif" alt="Uh ho! no scroll into focused option"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Uh oh ... The list doesn't scroll into the focused option, and it disappears from the visible area. So we need to update the scroll position when the focused item changes. To solve this, I first rolled up my sleeves and came up with my own implementation using &lt;code&gt;Dom.getViewportOf&lt;/code&gt;, &lt;code&gt;Dom.getElement&lt;/code&gt;, &lt;code&gt;Dom.setViewportOf&lt;/code&gt; and calculating offset positions. 🤯&lt;/p&gt;

&lt;p&gt;I was very proud of my &lt;em&gt;smart&lt;/em&gt; solution, only to discover later that browsers have this behaviour &lt;strong&gt;built-in&lt;/strong&gt; for focused elements. 😲🤦‍♀️&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus" rel="noopener noreferrer"&gt;MDN docs&lt;/a&gt;, &lt;code&gt;focus&lt;/code&gt; method on the element:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;will scroll the element into the visible area of the browser window&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;sup&gt; &lt;strong&gt;Wow! Browsers are awesome!&lt;/strong&gt; 💪 &lt;/sup&gt;&lt;br&gt;
By setting focus on the option while navigating with keys, we will automatically get the desired scroll behaviour, so let's do exactly that!&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus option on navigation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Browser.Dom&lt;/code&gt; exposes &lt;a href="https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom#focus" rel="noopener noreferrer"&gt;&lt;code&gt;focus&lt;/code&gt;&lt;/a&gt; that accepts the element's id and attempts to focus it. We will use &lt;a href="https://package.elm-lang.org/packages/elm/core/latest/Task#attempt" rel="noopener noreferrer"&gt;&lt;code&gt;Task.attempt&lt;/code&gt;&lt;/a&gt; to transform &lt;code&gt;Task&lt;/code&gt; returned from &lt;code&gt;focus&lt;/code&gt; into a command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Task&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;KeyPress&lt;/span&gt; &lt;span class="kt"&gt;KeyPressed&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;NoOp&lt;/span&gt;


&lt;span class="n"&gt;focusOption&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;focusOption&lt;/span&gt; &lt;span class="n"&gt;optionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;NoOp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Dom&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;focus&lt;/span&gt; &lt;span class="n"&gt;optionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;And let's use &lt;code&gt;focusOption&lt;/code&gt; each time we navigate with arrow keys: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="n"&gt;handleKeyWhenOpen&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
        &lt;span class="kt"&gt;Up&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;navigateWithKey&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getPrevId&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kt"&gt;Down&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;navigateWithKey&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getNextId&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;


&lt;span class="n"&gt;navigateWithKey&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;navigateWithKey&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;-- here we use focusOption&lt;/span&gt;
    &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;focusedId&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="n"&gt;focusOption&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Maybe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withDefault&lt;/span&gt; &lt;span class="kt"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;For this to work, each &lt;code&gt;li&lt;/code&gt; in our &lt;code&gt;html&lt;/code&gt; needs to have an id and tab index to be focusable. So let's modify our view accordingly:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Attributes&lt;/span&gt; &lt;span class="k"&gt;exposing&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tabindex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;viewOption&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Option&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;viewOption&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;li&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tabindex&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="n"&gt;text&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;I also cheated a bit here and added &lt;code&gt;scroll-behavior: smooth;&lt;/code&gt; in my &lt;code&gt;css&lt;/code&gt; to make scrolling look nicer, because why not?&lt;/p&gt;

&lt;p&gt;Now, we have one more thing left - close the dropdown on focus out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle focus out
&lt;/h2&gt;

&lt;p&gt;In my previous &lt;a&gt;post&lt;/a&gt;, I showed how to decode event object to close dropdown on click outside. In short, the decoder takes &lt;code&gt;event.target&lt;/code&gt;, traverses the DOM tree and for each element checks whether its id matches the id of the dropdown, if it finds a match - the event happened inside the dropdown.&lt;/p&gt;

&lt;p&gt;Let's use the same approach, but for &lt;code&gt;focusout&lt;/code&gt; event and &lt;code&gt;relatedTarget&lt;/code&gt; property on the event object. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget" rel="noopener noreferrer"&gt;&lt;code&gt;relatedTarget&lt;/code&gt;&lt;/a&gt; in this case will be the element receiving focus. We will attach a custom event listener using &lt;code&gt;on&lt;/code&gt; from &lt;code&gt;Browser.Events&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;

&lt;span class="n"&gt;viewDropdown&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Html&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;viewDropdown&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;div&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dropdown"&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preventDefaultOn&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keydown"&lt;/span&gt; &lt;span class="n"&gt;keyDecoder&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;focusout"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onFocusOut&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dropdown"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;onFocusOut&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;onFocusOut&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;outsideTarget&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relatedTarget"&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Here &lt;code&gt;outsideTarget&lt;/code&gt; is a decoder that will answer the question: &lt;strong&gt;is an element outside the dropdown taking over focus?&lt;/strong&gt; and dispatch the message that closes the dropdown. &lt;a href="https://github.com/MargaretKrutikova/elm-accessible-dropdown/blob/master/src/AccessibleDropdown.elm#L320" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the source code for the decoder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finale
&lt;/h2&gt;

&lt;p&gt;We have now implemented keyboard support for our dropdown. What is left to comply with the accessibility requirements is to add the appropriate &lt;code&gt;ARIA&lt;/code&gt; attributes and test the dropdown with a screen reader.&lt;/p&gt;

&lt;p&gt;For my implementation of the dropdown, I consulted the following resources on accessibility, which might be helpful to improve it even further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role#Keyboard_interactions" rel="noopener noreferrer"&gt;keyboard interactions for a listbox&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.w3.org/TR/wai-aria-practices/examples/listbox/listbox-collapsible.html" rel="noopener noreferrer"&gt;aria practices, collapsible dropdown&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for stopping by! 💨&lt;/p&gt;

</description>
      <category>elm</category>
      <category>a11y</category>
      <category>dom</category>
    </item>
    <item>
      <title>Elm DOM node decoder to detect click outside </title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Thu, 30 May 2019 18:21:51 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/elm-dom-node-decoder-to-detect-click-outside-3ioh</link>
      <guid>https://dev.to/margaretkrutikova/elm-dom-node-decoder-to-detect-click-outside-3ioh</guid>
      <description>&lt;p&gt;The other day I was implementing a reusable dropdown on a side project in &lt;code&gt;elm&lt;/code&gt;, and wanted the dropdown to close when I click outside of it. &lt;/p&gt;

&lt;p&gt;I came up with an ad-hoc solution, that felt like 😕, so I asked the elm community on slack to point me in the right direction. The folks there came up with a couple of creative solutions, and went on to implement them for different edge cases. As en elm noob, I was thrilled to learn from more experienced elm devs, and decided to share what I have learned from that discussion in a couple of posts. &lt;/p&gt;

&lt;p&gt;This one is going to be about implementing a generic way to detect click outside using some advanced decoding mechanisms to get DOM nodes from the event object. &lt;/p&gt;

&lt;p&gt;How cool is that, &lt;strong&gt;decode a DOM node in elm&lt;/strong&gt;? 🤯&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution outline
&lt;/h2&gt;

&lt;p&gt;I will use a simple dropdown as an example, but it can be any other element that benefits from detecting click outside (autocomplete, menu, popover etc).&lt;/p&gt;

&lt;p&gt;The solution is to determine whether the DOM node that was clicked is located within the dropdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;subscribe to the global &lt;code&gt;mousedown&lt;/code&gt; event on the document,&lt;/li&gt;
&lt;li&gt;get the element that dispatched the event (event target),&lt;/li&gt;
&lt;li&gt;check whether the target element is a descendant of the dropdown by recursively traversing up the DOM tree,&lt;/li&gt;
&lt;li&gt;if there is a match (either by id, className or any other attribute), the click happened within the dropdown - keep it open.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;DOM API has &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/contains"&gt;Node.contains&lt;/a&gt; that checks whether a node is a descendant of another node. This function doesn't seem to exist in Elm, but no worry, we are going to roll our own implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Subscribe to global &lt;code&gt;mousedown&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://package.elm-lang.org/packages/elm/browser/latest/Browser-Events"&gt;Browser.Events&lt;/a&gt; package allows to attach listeners to events on the whole document. It exposes &lt;code&gt;onMouseDown&lt;/code&gt; listener, that accepts a JSON decoder to decode the event object and sends a message with the result if decoding succeeds. &lt;/p&gt;

&lt;p&gt;To use JSON decoder install &lt;a href="https://package.elm-lang.org/packages/elm/json/latest/Json-Decode"&gt;Json-Decode package&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;elm&lt;/span&gt; &lt;span class="n"&gt;install&lt;/span&gt; &lt;span class="n"&gt;elm&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and use &lt;code&gt;onMouseDown&lt;/code&gt; in &lt;code&gt;subscriptions&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Events&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;

&lt;span class="n"&gt;subscriptions&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sub&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;subscriptions&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onMouseDown&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeed&lt;/span&gt; &lt;span class="kt"&gt;MouseDown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;NOTE: To use &lt;code&gt;subscriptions&lt;/code&gt;, the elm program should be at least of type &lt;code&gt;Browser.element&lt;/code&gt;, &lt;code&gt;Browser.sandbox&lt;/code&gt; can't talk to the outside world and doesn't support subscriptions, &lt;a href="https://guide.elm-lang.org/effects/"&gt;see effects on elm guide&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Our subscription doesn't do much right now: &lt;code&gt;Decode.succeed&lt;/code&gt; ignores the JSON event object and only sends &lt;code&gt;MouseDown&lt;/code&gt; message to the update function. &lt;/p&gt;

&lt;h2&gt;
  
  
  Recursive decoding of event target
&lt;/h2&gt;

&lt;p&gt;The event target of the &lt;code&gt;mousedown&lt;/code&gt; event is a DOM node that has a property &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/parentNode"&gt;&lt;code&gt;parentNode&lt;/code&gt;&lt;/a&gt;, which we can use to recursively traverse up the DOM tree until we find the dropdown node or reach the top of the tree. To determine whether the nodes match we will use their ids.&lt;/p&gt;

&lt;p&gt;I will first explain a more detailed implementation of the decoder before jumping to the eventual compact and elegant solution, which might seem quite cryptic at first glance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Naive decoding
&lt;/h3&gt;

&lt;p&gt;Here is a data structure that represents a DOM node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="kt"&gt;DomNode&lt;/span&gt;
    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;RootNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kt"&gt;ChildNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parentNode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;DomNode&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A decoder of this union type will use &lt;a href="https://package.elm-lang.org/packages/elm-lang/core/5.1.1/Json-Decode#lazy"&gt;&lt;code&gt;Decode.lazy&lt;/code&gt;&lt;/a&gt; for decoding recursive structures, and &lt;a href="https://package.elm-lang.org/packages/elm-lang/core/5.1.1/Json-Decode#oneOf"&gt;&lt;code&gt;Decode.oneOf&lt;/code&gt;&lt;/a&gt; for decoding the individual constructors of the union.&lt;/p&gt;

&lt;p&gt;In general, &lt;code&gt;Decode.oneOf&lt;/code&gt; is useful for decoding data that can be in different formats, and works by accepting different decoders and trying them in sequence until one of them succeeds.&lt;/p&gt;

&lt;p&gt;Here is the implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;domNode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;DomNode&lt;/span&gt;
&lt;span class="n"&gt;domNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oneOf&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;childNode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rootNode&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="n"&gt;rootNode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;DomNode&lt;/span&gt;
&lt;span class="n"&gt;rootNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;RootNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id"&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;childNode&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;DomNode&lt;/span&gt;
&lt;span class="n"&gt;childNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map2&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;parentNode&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;ChildNode&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parentNode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parentNode&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id"&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parentNode"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lazy&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;domNode&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trick here is the decoders that recursively reference each other: decoding &lt;code&gt;ChildNode&lt;/code&gt; references the &lt;code&gt;domNode&lt;/code&gt; decoder and requires using &lt;code&gt;Decode.lazy&lt;/code&gt;, and decoding &lt;code&gt;DomNode&lt;/code&gt; references the child node decoder. &lt;/p&gt;

&lt;p&gt;Now we can decode &lt;code&gt;target&lt;/code&gt; from the event in the subscriptions and send a message with the decoded &lt;code&gt;DomNode&lt;/code&gt;. The &lt;code&gt;update&lt;/code&gt; function will then recursively traverse the node up the tree and check for nodes that match the dropdown.&lt;/p&gt;

&lt;p&gt;But here comes the mind-blowing idea: &lt;strong&gt;what if we let the decoder determine whether the clicked node is inside the dropdown &lt;em&gt;while&lt;/em&gt; recursively traversing the tree?&lt;/strong&gt; 🤔 &lt;/p&gt;

&lt;p&gt;Instead of handing the whole recursive DOM structure to the update function, the decoder just answers the essential question of what to do with the dropdown: &lt;strong&gt;to close or not to close?&lt;/strong&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Outside-dropdown-decoder
&lt;/h3&gt;

&lt;p&gt;A better decoder will consist of a sequence of decoders traversing the DOM tree and making the decision on the fly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;isOutsideDropdown&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;Bool&lt;/span&gt;
&lt;span class="n"&gt;isOutsideDropdown&lt;/span&gt; &lt;span class="n"&gt;dropdownId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oneOf&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id"&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;
            &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;andThen&lt;/span&gt;
                &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dropdownId&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                        &lt;span class="c1"&gt;-- found match by id&lt;/span&gt;
                        &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeed&lt;/span&gt; &lt;span class="kt"&gt;False&lt;/span&gt;

                    &lt;span class="k"&gt;else&lt;/span&gt;
                        &lt;span class="c1"&gt;-- try next decoder&lt;/span&gt;
                        &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;continue"&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lazy&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;isOutsideDropdown&lt;/span&gt; &lt;span class="n"&gt;dropdownId&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;parentNode"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;-- fallback if all previous decoders failed&lt;/span&gt;
        &lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeed&lt;/span&gt; &lt;span class="kt"&gt;True&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The responsibilities of these decoders are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first decoder will check the node's id and succeed with &lt;code&gt;False&lt;/code&gt; (&lt;em&gt;inside dropdown&lt;/em&gt;) if it finds a match, or fail causing the other decoders to step in,&lt;/li&gt;
&lt;li&gt;the second decoder will recursively call the parent decoder, and might fail if the &lt;code&gt;parentNode&lt;/code&gt; is null (the top of the tree is reached), causing the last decoder to run,&lt;/li&gt;
&lt;li&gt;the last decoder simply succeeds with &lt;code&gt;True&lt;/code&gt; (&lt;em&gt;outside dropdown&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a side note, &lt;code&gt;Decode.fail&lt;/code&gt; takes in an arbitrary string which becomes a custom error message.&lt;/p&gt;

&lt;p&gt;We need one more decoder that gets &lt;code&gt;target&lt;/code&gt;, feeds it to the &lt;code&gt;isOutsideDropdown&lt;/code&gt; decoder and sends message &lt;code&gt;Close&lt;/code&gt; based on the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;outsideTarget&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Decoder&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;outsideTarget&lt;/span&gt; &lt;span class="n"&gt;dropdownId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;target"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isOutsideDropdown&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dropdown"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;andThen&lt;/span&gt;
            &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;\&lt;/span&gt;&lt;span class="n"&gt;isOutside&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isOutside&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
                    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;succeed&lt;/span&gt; &lt;span class="kt"&gt;Close&lt;/span&gt;

                &lt;span class="k"&gt;else&lt;/span&gt;
                    &lt;span class="kt"&gt;Decode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fail&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inside dropdown"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://package.elm-lang.org/packages/elm-lang/core/5.1.1/Json-Decode#andThen"&gt;&lt;code&gt;Decode.andThen&lt;/code&gt;&lt;/a&gt; is used to create a new decoder that depends on the result of &lt;code&gt;isOutsideDropdown&lt;/code&gt; decoder. &lt;/p&gt;

&lt;p&gt;Final step, we subscribe to &lt;code&gt;mousedown&lt;/code&gt; if the dropdown is open (to avoid listening to the event unnecessarily) and pass the id of the dropdown to the decoder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elm"&gt;&lt;code&gt;&lt;span class="n"&gt;subscriptions&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Model&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;Sub&lt;/span&gt; &lt;span class="kt"&gt;Msg&lt;/span&gt;
&lt;span class="n"&gt;subscriptions&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="kt"&gt;Browser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;onMouseDown&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;outsideTarget&lt;/span&gt; &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dropdown"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="kt"&gt;Sub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;none&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other applications of the decoder
&lt;/h2&gt;

&lt;p&gt;This technique of DOM node decoding is extremely powerful and can be extended to more than determining click outside. &lt;/p&gt;

&lt;p&gt;To improve keyboard accessibility of the dropdown, we need to close it when it loses focus (when "tabbing out" of it). One trick is to use the &lt;code&gt;focusout&lt;/code&gt; event and apply the same decoder to the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/relatedTarget"&gt;&lt;code&gt;relatedTarget&lt;/code&gt;&lt;/a&gt; property, which gives the element that is about to receive focus.  &lt;/p&gt;

&lt;p&gt;In my next post I will show how to make the dropdown more accessible and handle focus and keyboard events.&lt;/p&gt;




&lt;p&gt;Here is the source code on &lt;a href="https://github.com/MargaretKrutikova/elm-accessible-dropdown/tree/simple-dropdown"&gt;github&lt;/a&gt;, and here is a slightly trimmed version in &lt;a href="https://ellie-app.com/5GKDGkdr8tBa1"&gt;ellie&lt;/a&gt; for you to play with.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: none of this magic would have happened without the amazing elm community, and none of this would have mattered without you, reader!&lt;/em&gt; 😍 &lt;em&gt;To be continued...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;P.S. If by any chance, you need to implement detecting click outside in ReasonML, you can drop by &lt;a href="https://dev.to/margaretkrutikova/reasonreact-use-dom-api-and-hooks-to-detect-click-outside-4f74"&gt;my other post&lt;/a&gt;, where I explain how to create a custom &lt;code&gt;useClickOutside&lt;/code&gt; hook in a ReasonReact application.&lt;/p&gt;

&lt;p&gt;I seem to have a click-outside obsession... Anyway, thanks for reading! 😅 &lt;/p&gt;

</description>
      <category>elm</category>
      <category>decoder</category>
      <category>dom</category>
      <category>clickoutside</category>
    </item>
    <item>
      <title>Modelling remote data in ReasonReact</title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Thu, 23 May 2019 22:46:37 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/modelling-remote-data-in-reasonreact-4mpf</link>
      <guid>https://dev.to/margaretkrutikova/modelling-remote-data-in-reasonreact-4mpf</guid>
      <description>&lt;p&gt;Let's look at a very common way of modelling state for api data in a react app with &lt;code&gt;Typescript&lt;/code&gt;. You have an &lt;code&gt;isLoading&lt;/code&gt; flag, the actual &lt;code&gt;data&lt;/code&gt; and an &lt;code&gt;error&lt;/code&gt; property in case something goes wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Data&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure is easy to work with, but is far from perfect, since it allows to represent invalid states and can be error-prone when rendering the UI parts for a particular state (a bunch of &lt;code&gt;if&lt;/code&gt;s in the &lt;code&gt;render&lt;/code&gt; function).&lt;/p&gt;

&lt;p&gt;An example of such an error is setting initial data to an empty array of, let's say notifications, and forgetting to hide it while loading the actual notifications. This results in "you have no notifications" message, while it should show "loading notifications". This example is described in details in this great article &lt;a href="http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html"&gt;How Elm Slays a UI Antipattern&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When writing &lt;code&gt;reason&lt;/code&gt;, the language gives us a solid type system which allows to model remote data in a better way by using variants and pattern matching. With a proper data model, the language will also force us to give the user correct feedback for each possible state.&lt;/p&gt;

&lt;p&gt;In this article I want to show a way to model state for api data, while avoiding invalid states. I will show how to use the library &lt;a href="https://github.com/lrosa007/remotedata-re"&gt;&lt;code&gt;remotedata-re&lt;/code&gt;&lt;/a&gt;, and finally how to create our own abstraction over it to reuse it in different parts of the application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Remote data states
&lt;/h2&gt;

&lt;p&gt;The examples I am going to show to explain the concepts of remote data, are from a mini app I built - an advice generator with the api at &lt;a href="https://api.adviceslip.com/advice"&gt;api.adviceslip.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now you might think, this sounds silly :) But it was actually fun to play with the app, and some pieces of advice were really enlightening, like these ones:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Enjoy a little nonsense now and then.&lt;/li&gt;
&lt;li&gt;You don't need to floss all of your teeth. Only the ones you want to keep.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The source code for the app is in &lt;a href="https://github.com/MargaretKrutikova/advice-app-re"&gt;this repo&lt;/a&gt; on my github. In the app the user can search for advice by typing into a search input, which calls the api when the user presses &lt;code&gt;Enter&lt;/code&gt; and displays a list of hits.&lt;/p&gt;

&lt;p&gt;When making api calls, we are interested in the following states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't have any data to show and haven't made an api call yet,&lt;/li&gt;
&lt;li&gt;I don't have any data yet, but I have sent an api call and waiting for response (&lt;code&gt;loading&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;I have received an error from the server,&lt;/li&gt;
&lt;li&gt;I have received some data and can render it,&lt;/li&gt;
&lt;li&gt;I am re-fetching the data, (&lt;code&gt;loading&lt;/code&gt; but showing the old data to prevent content blinking).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Modelling states with a variant
&lt;/h2&gt;

&lt;p&gt;So we want to express those states and we start with a &lt;a href="https://reasonml.github.io/docs/en/variant"&gt;variant&lt;/a&gt;. The search api response and the search result could look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;searchResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;total_results&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;searchResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotAsked&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we have constrained the data type to be only in one of these states: loading with possible data of &lt;code&gt;searchResponse&lt;/code&gt;, failure with a &lt;code&gt;string&lt;/code&gt; and success with data of &lt;code&gt;searchResponse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Note how loading state can also hold data, which is going to be empty before the first search, but will have the previous search result on any subsequent search.&lt;/p&gt;

&lt;p&gt;If loading state doesn't carry any information, next time we search the current search result will disappear before the next response comes back. In some scenarios that might be okay or even useful (to prevent showing stale data e.g.), but in this case we don't want the screen jumping unnecessarily in between the calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using &lt;code&gt;RemoteData&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In order to reuse the above data structure, we could make it polymorphic and add a type parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;apiData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotAsked&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can create our type like &lt;code&gt;type searchResult = apiData(searchResponse)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But there is already a small and handy library called &lt;a href="https://github.com/lrosa007/remotedata-re"&gt;remotedata-re&lt;/a&gt; with a similar data type. The library also comes with a couple utility functions for working with this data structure. The type defined in &lt;code&gt;RemoteData&lt;/code&gt; looks very similar to our own &lt;code&gt;webData&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotAsked&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this structure, we can redefine &lt;code&gt;searchResult&lt;/code&gt;, and model our state like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResponse&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NotAsked&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Transition between states
&lt;/h2&gt;

&lt;p&gt;In order to transition between the states when making api calls, we need to define actions that will bear the information about the transition, and a reducer that will respond to those actions. &lt;/p&gt;

&lt;p&gt;Here is how it might look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchLoading&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&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="n"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchLoading&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;searchResult&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;withDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&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;When processing the loading state, I am using helper functions &lt;code&gt;RemoteData.map&lt;/code&gt; to apply the function &lt;code&gt;(d =&amp;gt; Some(d))&lt;/code&gt; to the underlying data if &lt;code&gt;searchResult&lt;/code&gt; is &lt;code&gt;Success&lt;/code&gt;, and &lt;code&gt;RemoteData.withDefault&lt;/code&gt; to "unwrap" the data from state &lt;code&gt;Success&lt;/code&gt;, or give back &lt;code&gt;None&lt;/code&gt; otherwise.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RemoteData.(...)&lt;/code&gt; opens the module locally and allows to refer to the module values inside the scope without prefixing them with &lt;code&gt;RemoteData&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Custom remote data type
&lt;/h2&gt;

&lt;p&gt;Usually a bigger app with several pages will need to perform different api calls at different points of time. So do we have to repeat that monster-block of code in our reducer when handling &lt;code&gt;Loading&lt;/code&gt;, &lt;code&gt;Error&lt;/code&gt; and &lt;code&gt;Success&lt;/code&gt; cases? &lt;/p&gt;

&lt;p&gt;I wanted to avoid doing so and, as an experiment, created a small abstraction over that piece of logic in a module called &lt;code&gt;WebData&lt;/code&gt; (name borrowed from an elm package &lt;code&gt;elm-web-data&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;apiAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;RequestLoading&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;RequestError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;RequestSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;toLoading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.(&lt;/span&gt;&lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;withDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;updateWebData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;apiAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;'&lt;/span&gt;&lt;span class="n"&gt;a&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="n"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;RequestLoading&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toLoading&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;RequestError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;RequestSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&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;Here I define a polymorphic type that already has &lt;code&gt;option('a)&lt;/code&gt; as &lt;code&gt;Loading&lt;/code&gt; state. I am also including an action type for transitioning between the states and a helper function to handle the actual transitions.&lt;/p&gt;

&lt;p&gt;Now we can modify the above code for search result like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;WebData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;)};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;WebData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;apiAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchResponse&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;reducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&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="n"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;SearchRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchAction&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="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;WebData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updateWebData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;searchAction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&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;This looks much cleaner! I am wrapping the api action for search result in a more specific variant &lt;code&gt;SearchRequest&lt;/code&gt;. Then when pattern matching over it, I can extract the underlying api action and pass it into the function &lt;code&gt;updateWebData&lt;/code&gt;, which gives back the new state for &lt;code&gt;searchResult&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This pattern was inspired by The Elm Architecture, where you can create a module that owns its own state and exposes its update function and message. When the module is plugged into the main program, its message is wrapped into a new constructor that is part of the global message, the global update function can then unwrap it and call the update function of that module with the underlying message that the module understands.&lt;/p&gt;

&lt;p&gt;In the advice generator app, the &lt;code&gt;WebData&lt;/code&gt; module is reused for both fetching search results and generating random advice, you can check the implementation &lt;a href="https://github.com/MargaretKrutikova/advice-app-re/tree/master/src"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Render remote data
&lt;/h2&gt;

&lt;p&gt;Let's see how we can pattern match all the possible states of our &lt;code&gt;state.searchResult&lt;/code&gt; and give the user correct feedback for each case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotAsked&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Information&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"You haven't searched yet!"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Spinner&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchResult&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Spinner&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchResult&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;Message&lt;/code&gt;, &lt;code&gt;Spinner&lt;/code&gt; and &lt;code&gt;SearchResult&lt;/code&gt; are components I defined in the app (source code &lt;a href="https://github.com/MargaretKrutikova/advice-app-re/tree/master/src"&gt;here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There is a bit of duplication going on here. &lt;code&gt;Success&lt;/code&gt; and &lt;code&gt;Loading&lt;/code&gt; with &lt;code&gt;Some(data)&lt;/code&gt; both use &lt;code&gt;SearchResult&lt;/code&gt; to render that data, but the actual rendering logic could be more complicated, so we might want to handle it in one case to avoid this duplication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;searchResult&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;NotAsked&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Information&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"You haven't searched yet!"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Spinner&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;true&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;searchState&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Spinner&lt;/span&gt; &lt;span class="n"&gt;show&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nn"&gt;RemoteData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;searchState&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchResult&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There can be different ways to render &lt;code&gt;RemoteData&lt;/code&gt; or &lt;code&gt;WebData&lt;/code&gt; with pattern matching and using helper functions from &lt;code&gt;remotedata-re&lt;/code&gt;, and they will most likely vary with different UI requirements (e.g. placement of the spinner, disabling other elements on the page while loading etc.).&lt;/p&gt;

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

&lt;p&gt;The key points are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using variants to model remote data in &lt;code&gt;ReasonMl&lt;/code&gt; helps to avoid invalid states,&lt;/li&gt;
&lt;li&gt;each constructor in the variant represents a particular state of an api call and can carry extra information (like &lt;code&gt;Success&lt;/code&gt; state with api data),&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;remotedata-re&lt;/code&gt; is a handy package that already implements a remote data type and exposes functions for working with it, &lt;/li&gt;
&lt;li&gt;you can create your own reusable abstractions to help manage api data throughout your application, &lt;/li&gt;
&lt;li&gt;rendering remote data involves pattern matching directly in your &lt;code&gt;jsx&lt;/code&gt; and the implementation might vary depending on the UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What patterns have you found useful when working with remote data in reason? Curious to hear about your experience and appreciate sharing it in the comments :) &lt;/p&gt;

</description>
      <category>reason</category>
      <category>react</category>
      <category>remotedata</category>
    </item>
    <item>
      <title>ReasonReact: use DOM API and hooks to detect click outside</title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Mon, 29 Apr 2019 22:12:01 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/reasonreact-use-dom-api-and-hooks-to-detect-click-outside-4f74</link>
      <guid>https://dev.to/margaretkrutikova/reasonreact-use-dom-api-and-hooks-to-detect-click-outside-4f74</guid>
      <description>&lt;p&gt;Working on a &lt;code&gt;reason-react&lt;/code&gt; application is an absolute delight. The language is perfectly suitable for writing the application's logic with reducers, especially with the latest version of &lt;code&gt;ReasonReact&lt;/code&gt; with simpler and more concise syntax for writing components with hooks.&lt;/p&gt;

&lt;p&gt;But when you need to do DOM manipulations, use &lt;code&gt;refs&lt;/code&gt; on DOM elements, attach some event handlers or work with event objects, it gets less pleasant. There are not many resources available, it is difficult to get the types right and compiler errors are sometimes not very helpful. &lt;/p&gt;

&lt;p&gt;In this article I want to show how to do all of the above without pain, while solving a very common problem: detecting a click outside of a DOM element. &lt;/p&gt;

&lt;p&gt;The end result will be a &lt;code&gt;useClickOutside&lt;/code&gt; hook, which takes in a function to run when a click is detected outside of an element, and returns a &lt;code&gt;ref&lt;/code&gt; that you need to attach to that element. The source code is in &lt;a href="https://github.com/MargaretKrutikova/use-click-outside-re"&gt;my github repo&lt;/a&gt; with an example usage of the hook, so feel free to check it out directly if you just need a working solution.&lt;/p&gt;

&lt;h1&gt;
  
  
  Use case
&lt;/h1&gt;

&lt;p&gt;There are quite a few reasons why you might want to detect clicks outside of an element. The most common is to hide an element when the user clicks outside of its area, like closing a modal, a dropdown, a notification etc. So here is a straight forward solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Listen to the &lt;code&gt;onmousedown&lt;/code&gt; event on the document,&lt;/li&gt;
&lt;li&gt;In the event handler get the element that dispatched the event (event target),&lt;/li&gt;
&lt;li&gt;Check whether the target element is a descendant of the main element that needs to react on click outside using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/contains"&gt;&lt;code&gt;Node.contains&lt;/code&gt;&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Call the function if it is not within the main element.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Implementation
&lt;/h1&gt;

&lt;p&gt;I am using the latest &lt;code&gt;ReasonReact&lt;/code&gt; version (&amp;gt;= 0.7.0) that allows using hooks, if you haven't used them already in &lt;code&gt;ReasonReact&lt;/code&gt;, I highly recommend checking out this &lt;a href="https://dev.to/iwilsonq/reasonml-with-react-hooks-tutorial-building-a-pomodoro-timer-57h0"&gt;article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the implementation we will use &lt;a href="https://github.com/reasonml-community/bs-webapi-incubator"&gt;&lt;code&gt;bs-webapi&lt;/code&gt;&lt;/a&gt; with &lt;code&gt;reason&lt;/code&gt; bindings to the DOM API and a couple of react hooks (&lt;code&gt;useRef&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;So let's embrace the &lt;code&gt;OCaml&lt;/code&gt; type system and dive right into the implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add dependencies
&lt;/h2&gt;

&lt;p&gt;Install &lt;code&gt;bs-webapi&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;bs-webapi &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and add it to the dependencies in &lt;code&gt;bsconfig.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"bs-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="s2"&gt;"reason-react"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bs-webapi"&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;h2&gt;
  
  
  Add event listener in useEffect
&lt;/h2&gt;

&lt;p&gt;Let's start implementing the &lt;code&gt;useClickOutside&lt;/code&gt; hook by adding a mousedown event listener in &lt;code&gt;useEffect&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nn"&gt;Webapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Dom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;useClickOutside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClickOutside&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseEvent&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;unit&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;let&lt;/span&gt; &lt;span class="n"&gt;handleMouseDown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&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="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useEffect0&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="nn"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;addMouseDownEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handleMouseDown&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;cleanup&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;unsubscribe&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;unmount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
    &lt;span class="nc"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;removeMouseDownEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handleMouseDown&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&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;Here &lt;code&gt;Document.addMouseDownEventListener&lt;/code&gt; and &lt;code&gt;document&lt;/code&gt; come from &lt;code&gt;Webapi.Dom&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We start listening to the &lt;code&gt;mousedown&lt;/code&gt; event on the &lt;code&gt;document&lt;/code&gt; inside &lt;code&gt;useEffect&lt;/code&gt; hook. &lt;code&gt;useEffect0&lt;/code&gt; means it has no dependencies and thus only runs once after the component is rendered the first time. &lt;/p&gt;

&lt;p&gt;In order to unsubscribe from the event, we can return a "cleanup" function from the effect. In &lt;code&gt;ReasonReact&lt;/code&gt; the type signature of the function in &lt;code&gt;useEffect&lt;/code&gt; is &lt;code&gt;(unit =&amp;gt; option(unit =&amp;gt; unit))&lt;/code&gt;, so we need to wrap our cleanup function in &lt;code&gt;Some&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with refs
&lt;/h2&gt;

&lt;p&gt;Now we define the &lt;code&gt;handleMouseDown&lt;/code&gt; function, which also needs to access a &lt;code&gt;ref&lt;/code&gt; to the main element which lets us determine the &lt;code&gt;outside&lt;/code&gt; area:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;elementRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Nullable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;handleClickOutside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elRef&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;handleMouseDown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseEvent&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="n"&gt;elementRef&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Nullable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;toOption&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nn"&gt;Belt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refValue&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refValue&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;onClickOutside&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="n"&gt;ignore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This looks cryptic ... What we are doing here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;define a &lt;code&gt;ref&lt;/code&gt; with &lt;code&gt;useRef&lt;/code&gt;, initialise it with &lt;code&gt;null&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;access the underline value of the reference with &lt;code&gt;React.Ref.current&lt;/code&gt; and convert it to option,&lt;/li&gt;
&lt;li&gt;use &lt;code&gt;Belt.Option.map&lt;/code&gt; to run &lt;code&gt;handleClickOutside&lt;/code&gt; only if the ref value is &lt;code&gt;Some&lt;/code&gt; and return the result wrapped in &lt;code&gt;Some&lt;/code&gt;, otherwise &lt;code&gt;None&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ignore&lt;/code&gt; to disregard the result returned from &lt;code&gt;Belt.Option.map&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I am using the fast pipe &lt;code&gt;-&amp;gt;&lt;/code&gt; here to apply an expression as the first argument to the functions. &lt;a href="https://dev.to/splodingsocks/a-quick-explanation-about---and--in-reasonml-5329"&gt;Here&lt;/a&gt; is a great article explaining how the fast pipe works if you are curious.&lt;/p&gt;

&lt;p&gt;There is more info on working with refs in &lt;a href="https://reasonml.github.io/reason-react/docs/en/refs"&gt;reason-react docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check if element is outside
&lt;/h2&gt;

&lt;p&gt;Great, almost done! Now we need to implement &lt;code&gt;handleClickOutside&lt;/code&gt; that will actually determine whether to call our custom function or not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;handleClickOutside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domElement&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;element&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Dom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mouseEvent&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fn&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;let&lt;/span&gt; &lt;span class="n"&gt;targetElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;EventTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;unsafeAsElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;domElement&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;targetElement&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="bp"&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;Here &lt;code&gt;domElement&lt;/code&gt; will determine the inside/outside boundary. It is important to mention that the mouse event in this case is not a react event (a.k.a. &lt;code&gt;Synthetic&lt;/code&gt; event), since we manually attached our callback to the document. In case of react mouse event you would use &lt;code&gt;ReactEvent.Mouse.t&lt;/code&gt;, in our case however we use &lt;code&gt;Dom.mouseEvent&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We will use &lt;code&gt;Element.contains&lt;/code&gt; to check whether the target element is a descendant of the &lt;code&gt;domElement&lt;/code&gt;. But here is a problem. This function takes in two parameters of type &lt;code&gt;Element&lt;/code&gt;, but the target element is of type &lt;code&gt;EventTarget&lt;/code&gt;, which strictly speaking, is not always an element and could for example be of type &lt;code&gt;XMLHttpRequest&lt;/code&gt; (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget"&gt;mdn docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;However, since we attached the event handler to a DOM element we know for sure it is an element and can use &lt;code&gt;EventTarget.unsafeAsElement&lt;/code&gt; to convert it to one.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/MargaretKrutikova/use-click-outside-re/blob/master/src/ClickOutside.re"&gt;link&lt;/a&gt; with the complete code of &lt;code&gt;useClickOutside&lt;/code&gt; hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example usage
&lt;/h2&gt;

&lt;p&gt;Here is how the hook can be used in the wild:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;open&lt;/span&gt; &lt;span class="nc"&gt;ClickOutside&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;react&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;component&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&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;let&lt;/span&gt; &lt;span class="n"&gt;handleClickOutside&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&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="nn"&gt;Js&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Click outside detected"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;divRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;useClickOutside&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handleClickOutside&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="n"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nn"&gt;ReactDOMRe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nn"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;domRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;divRef&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I have created a simple dropdown component to show a real use-case scenario, source code on &lt;a href="https://github.com/MargaretKrutikova/use-click-outside-re/blob/master/src/Dropdown.re"&gt;github&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;I hope this article can help beyond this specific case of detecting click outside by providing some helpful tips and explanations when it comes to working with the DOM API. &lt;/p&gt;

&lt;p&gt;Have you found anything that helped you? Or are you having trouble with DOM manipulations and refs while solving your particular case? Let me know by leaving a comment and we will figure it out :)&lt;/p&gt;

</description>
      <category>reason</category>
      <category>react</category>
      <category>hooks</category>
      <category>clickoutside</category>
    </item>
    <item>
      <title>How to: mobx-state-tree + react + typescript</title>
      <dc:creator>Margarita Krutikova</dc:creator>
      <pubDate>Sat, 06 Apr 2019 21:20:45 +0000</pubDate>
      <link>https://dev.to/margaretkrutikova/how-to-mobx-state-tree-react-typescript-3d5j</link>
      <guid>https://dev.to/margaretkrutikova/how-to-mobx-state-tree-react-typescript-3d5j</guid>
      <description>&lt;p&gt;This is a walkthrough on how to get a full setup with &lt;code&gt;mobx-state-tree&lt;/code&gt; and &lt;code&gt;react&lt;/code&gt; in a &lt;code&gt;CRA&lt;/code&gt; app with &lt;code&gt;typescript&lt;/code&gt;. This guide doesn't focus too much on the theory or how things work under the hood and mostly includes practical examples (code!) on &lt;em&gt;how to&lt;/em&gt; make things work.&lt;/p&gt;

&lt;p&gt;I have been mostly using &lt;code&gt;redux&lt;/code&gt; in all of my work and side projects, and eventually got curios about the other side of the state management world with &lt;code&gt;mobx&lt;/code&gt; and decided to jump right into &lt;code&gt;mobx-state-tree&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Trying to make &lt;code&gt;mobx-state-tree&lt;/code&gt; work in &lt;code&gt;react&lt;/code&gt; with &lt;code&gt;typescript&lt;/code&gt; appeared to be quite a struggle. Especially making everything properly typed (no cheating with &lt;code&gt;any&lt;/code&gt;!) in &lt;code&gt;Typescript&lt;/code&gt; was a challenge, so when eventually everything fell in place I thought I would share my setup in order to (hopefully) make someone else's life easier :)&lt;/p&gt;

&lt;p&gt;The application I build is a simple poll maker that allows to create a new poll, publish it, view and delete published polls. The source code with a cute little demo is available on my &lt;a href="https://github.com/MargaretKrutikova/mst-react-ts-guide"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the quick links to jump to directly if you have a particular problem that is covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Setup stores in mobx-state-tree

&lt;ul&gt;
&lt;li&gt;Create a base model&lt;/li&gt;
&lt;li&gt;Use composition to create domain stores&lt;/li&gt;
&lt;li&gt;CRUD on models in a nested list&lt;/li&gt;
&lt;li&gt;Convert between models&lt;/li&gt;
&lt;li&gt;Root store&lt;/li&gt;
&lt;li&gt;Communicate between stores&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Connect react to mobx

&lt;ul&gt;
&lt;li&gt;Why not mobx-react&lt;/li&gt;
&lt;li&gt;mobx-react-lite to the rescue&lt;/li&gt;
&lt;li&gt;Context provider to pass store&lt;/li&gt;
&lt;li&gt;Custom hook to inject stores&lt;/li&gt;
&lt;/ul&gt;


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

&lt;h2&gt;
  
  
  Setup stores in mobx-state-tree
&lt;/h2&gt;

&lt;p&gt;I started developing my app with designing stores of the domain area in &lt;code&gt;mobx-state-tree&lt;/code&gt; and was immediately faced with the following "how-to"s:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how to create a base model and use composition to extend it with properties and functionality in different stores,&lt;/li&gt;
&lt;li&gt;how to create a store with a nested list of items representing another model and perform CRUD operations on it,&lt;/li&gt;
&lt;li&gt;how to create a root store composing all the other domain stores,&lt;/li&gt;
&lt;li&gt;how to communicate between stores.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I figured those might be common problems when designing stores for any domain area, so I will go through them in more detail and show my solutions.&lt;/p&gt;

&lt;p&gt;In my poll-maker app there is going to be a base model &lt;code&gt;PollBase&lt;/code&gt;, a store responsible for creating a new poll &lt;code&gt;PollDraft&lt;/code&gt;, a model for a published poll &lt;code&gt;PublishedPoll&lt;/code&gt; and a store for published polls &lt;code&gt;PublishedPolls&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a base model
&lt;/h3&gt;

&lt;p&gt;Before we start, install the necessary dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add mobx mobx-state-tree
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now let's create a base model for the domain object &lt;code&gt;poll&lt;/code&gt;, which will have a poll question and a list of choices, and abase model for choice with a string property and id:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&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;types&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;mobx-state-tree&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;PollChoiceBase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PollChoiceBase&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;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&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;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PollBase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PollBase&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;question&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;choices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PollChoiceBase&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;h3&gt;
  
  
  Use composition to create domain stores
&lt;/h3&gt;

&lt;p&gt;A poll that is being edited (let's call it a draft poll) and not yet published will have the same properties as &lt;code&gt;PollBase&lt;/code&gt;, but also actions to edit those properties. Similar, a choice of a draft poll will have the same shape as &lt;code&gt;PollChoiceBase&lt;/code&gt; with an action to update it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PollDraftChoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PollChoiceBase&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="nb"&gt;self&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;setChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;self&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;choice&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;PollDraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PollBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PollDraftChoice&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&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;setQuestion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;question&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;A published poll can no longer be edited, so it won't have editing actions but it needs an extra property &lt;code&gt;id&lt;/code&gt; to be able to find it or create an external link to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PublishedPoll&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;PollBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&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="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identifier&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;h3&gt;
  
  
  CRUD on models in a nested list
&lt;/h3&gt;

&lt;p&gt;A draft poll has a list of choices, that can be added, edited and removed. Currently we have an action to update a choice (&lt;code&gt;setChoice&lt;/code&gt;), but no actions to remove an existing choice or add a new one.&lt;/p&gt;

&lt;p&gt;Here adding is rather trivial, but removal is a bit tricky. We want to be able to use &lt;code&gt;choice.remove()&lt;/code&gt; somewhere in a &lt;code&gt;react&lt;/code&gt; component, but actions can only modify the model they belong to or their children, so a choice can't simply remove itself and can only be removed by its parent &lt;code&gt;PollDraft&lt;/code&gt; since it "owns" the list of choices. This means &lt;code&gt;PollDraftChoice&lt;/code&gt; model will need a &lt;code&gt;remove&lt;/code&gt; action which will delegate its removal to &lt;code&gt;PollDraft&lt;/code&gt;, which we can retrieve via &lt;code&gt;getParent&lt;/code&gt; helper from &lt;code&gt;mobx-state-tree&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is the code (I use &lt;a href="https://github.com/dylang/shortid"&gt;shortid&lt;/a&gt; to generate unique ids):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&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;destroy&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getParent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cast&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;mobx-state-tree&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;// Instance is a typescript helper that extracts the type of the model instance&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PollDraftChoiceModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;PollDraftChoice&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PollDraftModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;PollDraft&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PollDraftChoice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PollChoiceBase&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="nb"&gt;self&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;remove&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;pollDraftParent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getParent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PollDraftModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&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="nx"&gt;pollDraftParent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PollDraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(...)&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="nb"&gt;self&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;addChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;choice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&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="nx"&gt;shortid&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;choice&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;removeChoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;choiceToRemove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PollDraftChoiceModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;choiceToRemove&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;Here is what is happening inside &lt;code&gt;PollDraftChoice&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getParent&amp;lt;PollDraftModel&amp;gt;(self, 2)&lt;/code&gt; means fetch parent 2 levels up - one until you reach &lt;code&gt;items&lt;/code&gt; property and one more until you reach &lt;code&gt;PollDraft&lt;/code&gt; itself, and assume that the returned parent is of type &lt;code&gt;PollDraftModel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pollDraftParent.removeChoice(cast(self))&lt;/code&gt; uses &lt;a href="https://github.com/mobxjs/mobx-state-tree/blob/master/docs/API/README.md#cast"&gt;&lt;code&gt;cast&lt;/code&gt;&lt;/a&gt; helper to tell typescript that &lt;code&gt;self&lt;/code&gt; is indeed of type &lt;code&gt;PollDraftChoiceModel&lt;/code&gt;. Why is it necessary? The problem is that &lt;code&gt;self&lt;/code&gt; here is of type what was &lt;a href="https://github.com/mobxjs/mobx-state-tree#typing-self-in-actions-and-views"&gt;before&lt;/a&gt; views and actions are applied, which means at that point &lt;code&gt;self&lt;/code&gt; is actually not of type &lt;code&gt;PollDraftChoiceModel&lt;/code&gt;, so &lt;code&gt;pollDraftParent.removeChoice(self)&lt;/code&gt; won't compile in TS.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Convert between models
&lt;/h3&gt;

&lt;p&gt;Let's create our second domain store to keep track of published polls:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&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;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getSnapshot&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;mobx-state-tree&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PublishedPollModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;PublishedPoll&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;PollDraftModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;PollDraft&lt;/span&gt;&lt;span class="o"&gt;&amp;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;PublishedPolls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;polls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PublishedPoll&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;actions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&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;publishDraft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;pollDraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SnapshotIn&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PollDraftModel&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pollToPublish&lt;/span&gt; &lt;span class="o"&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;pollDraft&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="nx"&gt;shortid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;polls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollToPublish&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;Here &lt;code&gt;publishDraft&lt;/code&gt; takes in a &lt;code&gt;snapshot&lt;/code&gt; of a poll draft. &lt;a href="https://github.com/mobxjs/mobx-state-tree#snapshots"&gt;Snapshot&lt;/a&gt; in &lt;code&gt;mobx-state-tree&lt;/code&gt; is a plain object stripped from all type information and actions and can be automatically converted to models. &lt;/p&gt;

&lt;p&gt;So why does &lt;code&gt;publishDraft&lt;/code&gt; need to take in a snapshot and not just &lt;code&gt;PollDraftModel&lt;/code&gt;? That's because an instance of &lt;code&gt;PollDraftModel&lt;/code&gt; can't be converted to a published poll since it will have extra actions that aren't compatible with &lt;code&gt;PublishedPollModel&lt;/code&gt;, and will cause a runtime exception. So, by specifying &lt;code&gt;SnapshotIn&amp;lt;PollDraftModel&amp;gt;&lt;/code&gt; we explicitly say that we want the raw data that exists on &lt;code&gt;PollDraftModel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next problem is that &lt;code&gt;publishDraft&lt;/code&gt; action has to be called somewhere from outside, either from the &lt;code&gt;PollDraft&lt;/code&gt; store or from some kind of &lt;code&gt;RootStore&lt;/code&gt;. Let's see how we can make that happen and establish some communication between the two stores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Root store
&lt;/h3&gt;

&lt;p&gt;Let's create a root store to combine all stores used in the app: &lt;code&gt;PollDraft&lt;/code&gt; and &lt;code&gt;PublishedPolls&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RootStoreModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;RootStore&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RootStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RootStore&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;pollDraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PollDraft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;publishedPolls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PublishedPolls&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Communicate between stores
&lt;/h3&gt;

&lt;p&gt;One way of communicating between stores, is to use &lt;code&gt;getRoot&lt;/code&gt; from &lt;code&gt;mobx-state-tree&lt;/code&gt; to fetch the root store and from there get the necessary store, or use &lt;code&gt;getParent&lt;/code&gt; to traverse the tree. This works fine for tightly coupled stores (like &lt;code&gt;PollDraft&lt;/code&gt; and &lt;code&gt;PollDraftChoice&lt;/code&gt;), but won't scale if used in more decoupled stores.&lt;/p&gt;

&lt;p&gt;One way to enable store communication is to make use of &lt;code&gt;getEnv&lt;/code&gt; function that can inject environment specific data when creating a state tree (from &lt;a href="https://github.com/mobxjs/mobx-state-tree#dependency-injection"&gt;mobx-state-tree docs&lt;/a&gt;). So we can just inject a newly created store into the whole state tree. One caveat here is that the environment can't be passed directly into one of the child stores and needs to be passed into the root store, otherwise you get this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;Error: &lt;span class="o"&gt;[&lt;/span&gt;mobx-state-tree] A state tree cannot be made part of another state tree 
as long as their environments are different.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Let's create a function called &lt;code&gt;createStore&lt;/code&gt;, similar to &lt;code&gt;redux&lt;/code&gt;'s &lt;code&gt;configureStore&lt;/code&gt;, that would create all individual stores, create the environment and assemble them all together in one root store. The environment will have only one property of &lt;code&gt;PublishedPolls&lt;/code&gt; store since it needs to be accessed from &lt;code&gt;PollDraft&lt;/code&gt; when publishing a poll draft:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;RootStoreEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;publishedPolls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PublishedPollsModel&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;createStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;RootStoreModel&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;publishedPolls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PublishedPolls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&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;pollDraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PollDraft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootStoreEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;publishedPolls&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;RootStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;pollDraft&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;publishedPolls&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;env&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;Now, &lt;code&gt;PolLDraft&lt;/code&gt; store can define a &lt;code&gt;publish&lt;/code&gt; action and call &lt;code&gt;publishDraft&lt;/code&gt; on &lt;code&gt;publishedPolls&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&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;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getEnv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getSnapshot&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;mobx-state-tree&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;PollDraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(...)&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="nb"&gt;self&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;publish&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;snapshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&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;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getEnv&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RootStoreEnv&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishedPolls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publishDraft&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;snapshot&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;h3&gt;
  
  
  Connect to redux devtools
&lt;/h3&gt;

&lt;p&gt;We will use &lt;code&gt;connectReduxDevtools&lt;/code&gt; middleware from the package &lt;code&gt;mst-middlewares&lt;/code&gt; that will connect the state tree to the redux devtools (more info and configuration options available in the &lt;a href="https://github.com/mobxjs/mobx-state-tree/tree/master/packages/mst-middlewares#connectreduxdevtools"&gt;docs&lt;/a&gt;). In order to setup the connection we will use a monitoring tool &lt;a href="https://github.com/zalmoxisus/remotedev"&gt;&lt;code&gt;remotedev&lt;/code&gt;&lt;/a&gt;. Install the packages first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; remotedev mst-middlewares
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;and add the following code after the store creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&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;createStore&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;../stores/createStore&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;connectReduxDevtools&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;mst-middlewares&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;rootStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;connectReduxDevtools&lt;/span&gt;&lt;span class="p"&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="s2"&gt;remotedev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;rootStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Connect react to mobx
&lt;/h2&gt;

&lt;p&gt;The part I struggled most with is how to connect &lt;code&gt;react&lt;/code&gt; to &lt;code&gt;mobx&lt;/code&gt; and start using stores in my components. The idea here is that react components need to become "reactive" and start tracking observables from the stores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why NOT mobx-react
&lt;/h3&gt;

&lt;p&gt;The most common way to achieve this is by using &lt;a href="https://github.com/mobxjs/mobx-react"&gt;mobx-react&lt;/a&gt; which provides &lt;code&gt;observer&lt;/code&gt; and &lt;code&gt;inject&lt;/code&gt; functions, where &lt;code&gt;observer&lt;/code&gt; is wrapped around components to make them react to changes and re-render and &lt;code&gt;inject&lt;/code&gt; just injects stores into components. However, I wouldn't recommend using this library because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when using &lt;code&gt;observer&lt;/code&gt;, the component loses the ability to use hooks because it gets converted to a class, more on this &lt;a href="https://github.com/mobxjs/mobx-react/issues/594"&gt;here&lt;/a&gt;. And the docs recommend in the &lt;a href="https://mobx.js.org/best/pitfalls.html"&gt;best practices&lt;/a&gt; to use &lt;code&gt;observer&lt;/code&gt; around as many components as possible, which means hooks can't be used almost anywhere,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inject&lt;/code&gt; function is quite compilcated and doesn't work well with typescript (see &lt;a href="https://github.com/mobxjs/mobx-react/issues/256#issuecomment-433587230"&gt;github issue&lt;/a&gt;), requiring all stores to be marked as optional and then using &lt;code&gt;!&lt;/code&gt; to indicate that they actually exist.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  mobx-react-lite to the rescue
&lt;/h3&gt;

&lt;p&gt;Luckily there is another library, &lt;a href="https://github.com/mobxjs/mobx-react-lite"&gt;&lt;code&gt;mobx-react-lite&lt;/code&gt;&lt;/a&gt;, which is built with hooks and provides &lt;code&gt;observer&lt;/code&gt; wrapper. One thing worth mentioning, &lt;code&gt;observer&lt;/code&gt; doesn't support classes, but there is a dedicated component &lt;code&gt;Observer&lt;/code&gt; that can be wrapped around parts of &lt;code&gt;jsx&lt;/code&gt; in render in class components.&lt;/p&gt;

&lt;p&gt;It is easy to get confused with this library since it provides a lot of hooks like &lt;code&gt;useObservable&lt;/code&gt;, &lt;code&gt;useComputed&lt;/code&gt; etc. that are going to be deprecated according to &lt;a href="https://github.com/mobxjs/mobx-react-lite#notice-of-deprecation"&gt;the docs&lt;/a&gt;. Instead here is a &lt;a href="https://github.com/mobxjs/mobx-react/issues/256#issuecomment-433587230"&gt;recommended way&lt;/a&gt;, that we are going to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use &lt;code&gt;react context&lt;/code&gt; provider to pass down the store(s),&lt;/li&gt;
&lt;li&gt;access the store using &lt;code&gt;useContext&lt;/code&gt; hook with a selector, alternatively inject the necessary stores with a custom &lt;code&gt;useInject&lt;/code&gt; hook based on the &lt;code&gt;useContext&lt;/code&gt; hook,&lt;/li&gt;
&lt;li&gt;wrap components with &lt;code&gt;observer&lt;/code&gt; from &lt;code&gt;mobx-react-lite&lt;/code&gt; to subscribe to changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So let's install the library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add mobx-react-lite
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Context provider to pass store
&lt;/h3&gt;

&lt;p&gt;First, let's create context &lt;code&gt;StoreContext&lt;/code&gt;, that will later receive the root store as its &lt;code&gt;value&lt;/code&gt;, and export provider and a custom hook for accessing the context value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StoreContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RootStoreModel&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;as&lt;/span&gt; &lt;span class="nx"&gt;RootStoreModel&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;useStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;StoreContext&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;StoreProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;StoreContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then create the root store with &lt;code&gt;createStore&lt;/code&gt; and send it into &lt;code&gt;StoreProvider&lt;/code&gt; which we wrap around &lt;code&gt;App&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&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;StoreProvider&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;./StoreProvider&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;createStore&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;../stores/createStore&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;rootStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createStore&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;Root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StoreProvider&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;rootStore&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;StoreProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom hook to inject stores
&lt;/h3&gt;

&lt;p&gt;It is possible to use the &lt;code&gt;useStore&lt;/code&gt; hook directly to access the root store and get the necessary data from it, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pollDraft&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I also implemented a &lt;code&gt;useInject&lt;/code&gt; hook that takes in a mapping function and returns a mapped object, similar to how it is done in &lt;code&gt;redux&lt;/code&gt; with &lt;code&gt;mapStateToProps&lt;/code&gt;. This hook is somewhat close to the idea of &lt;a href="https://github.com/mobxjs/mobx-react#customizing-inject"&gt;custom inject&lt;/a&gt; with a mapper function, but with hooks. So if you have a more complicated app with lots of things in your store, you might want to get only the things you want and not care about the rest.&lt;/p&gt;

&lt;p&gt;In its simplest form, &lt;code&gt;useInject&lt;/code&gt; hook might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MapStore&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootStoreModel&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;T&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useInject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;(mapStore: MapStore&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;) =&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useStore&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;mapStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="si"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PollDraft&lt;/code&gt; component would then use &lt;code&gt;useInject&lt;/code&gt; to access &lt;code&gt;pollDraft&lt;/code&gt; store from the root store:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight jsx"&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;observer&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;mobx-react-lite&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;RootStoreModel&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;../stores/RootStore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;useInject&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../hooks/useInject&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;mapStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootStore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootStoreModel&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="na"&gt;pollDraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pollDraft&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;PollDraft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FunctionComponent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pollDraft&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useInject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mapStore&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Create a new poll&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pollDraft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onChange=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;pollDraft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setQuestion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&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="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pollDraft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publish&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Publish&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is especially useful if &lt;code&gt;mapStore&lt;/code&gt; function is more complicated and involves combining data and actions from several stores.&lt;/p&gt;




&lt;p&gt;At this point I felt like I covered the basics and created a setup that I could continue building upon or use it as a boilerplate for projects with a similar stack. The source code can be found on my &lt;a href="https://github.com/MargaretKrutikova/mst-react-ts-guide"&gt;github&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;I hope this walkthrough was useful and you found something that helped you in your projects. Would love to hear your feedback on what you think was helpful or share your own experience with &lt;code&gt;mobx-state-tree&lt;/code&gt; and &lt;code&gt;react&lt;/code&gt; in &lt;code&gt;typescript&lt;/code&gt;!&lt;/p&gt;

</description>
      <category>mobxstatetree</category>
      <category>typescript</category>
      <category>react</category>
    </item>
  </channel>
</rss>
