Elmish logo

My tips for working with Elmish

This article is intended for people who are already familiar with the MVU architecture. You can learn more about it on the Elmish website.


I am one of the maintainer of Elmish and I created several Fable / Elmish libraries and applications. When doing so, I needed to understand Elmish deeply and I want to take the opportunity of F# Advent Calendar to share with you my tips and knowledge.

I will be covering a lot in this article, so grab yourself a cup of tea 🍵, take a deep breath 💨 and let’s get started 🏁.


Commands

A command is a container for a function that Elmish executes immediately but, may schedule dispatch of message at any time.

For example:

  • Issue immediately a new Message
  • Make an HTTP call and then return the result in your application via a Message
  • Save data in your storage

Basic usage

In this section, I will cover all the default functions offered by Elmish and show an example of it.

Use Cmd.none to schedule no Commands.

Use Cmd.ofMsg to schedules another message directly, it can be seen as a way to chain messages.

Use Cmd.map to map a command to another type. In general, you use this function when working with nested components.

UseCmd.ofAsync to evaluate an async block and map the result into success or error (of exception).

Use Cmd.ofFunc to evaluate a simple function and map the result into success or error (of exception).

UseCmd.performFunc to evaluate a simple function and map the success to a message discarding any possible error.

UseCmd.attemptFunc to evaluate a simple function and map the error (in case of exception)

Use Cmd.ofPromise to call promise block and map the results.

Use Cmd.ofSub to call the subscriber. This is useful when you are dealing with an API which use callbacks or to listen to an event.

Use Cmd.batch to aggregate multiple commands. You can aggregate any of the previous commands together.

The functions provided out of the box by Elmish are enough to cover most of the needs of your application. However, when working on a library or with a specific API you could want to create your own commands. We will take a look at it later.

Modelize your Model according to your needs

When working with a Single Page Application, you will have to deal with navigation and need to reflect it in your models.

In general, when I am working with at a page level, I use Discrimination Union so I can store only the active page state. 
For example, if in your application you have a login page you can’t store your authenticated page state in your application because you can’t fetch the resource to display it.

In general my main Model looks like this:

Using DUs to represent your pages state helps you isolate your logics. For me, the biggest benefit compared to storing all page states in a big record, is that you are not caching your page when navigating.

Of course sometimes your application needs to maintain state between navigation. For example, if you have a form in several steps you could modelize it like this:

Everything is a function

One thing we tend to forget when working with Elmish is that everything is a function. The main reason for this forgetfulness is that most of the examples only show you this code:

So in our mind update takes two arguments when in fact we should think update takes at least two arguments. The same applies to view and init functions.

This idea is linked to the fact that a component’s Model should have all the information needed by the component, but this is not always true.

Pass data as an argument

For example, if you have a session in your application in order to make HTTP calls, should you store this session in each component’s Model ?
No, you should store it at one place and then pass your session to the functions that need it.

The next code illustrates this situation, we request a session argument in our init function because we need to make an authenticated http request using Http.Auth.* (custom module used, includes the session info in a request).

Pass a record as an argument

If your view function takes several arguments you can use a record as an argument. It will force you to name the arguments and by doing so make your code easier to read and maintain over time.

It will also make it easier to optimize your code for react using memoBuilder.

Use helper function to work with your Domain

For example, when an elmish component needs to fetch data from the server often, I like to show a loading animation. Here is how I handle it:

As you can see I use a DU to represent if the component is Loaded , Loading or in an Errored state. And in order to simplify my update function, I use the applyIfLoaded function. This helps me keep the update code simple and easy to read.

Make public bare minimum

By default in F# everything is public, so you need to explicitly use private keyword for your functions and types to restrict their access.

As you can see, I am only exposing init , update and view functions to the parent. This will allow easier refactoring and easier usability for you or others because you can directly see what API is available.

Make the child communicate with the parent

In general, a child should be independent from its parent and doesn’t send messages to it. However, this is sometimes needed, for example if you have a Login component you want to be able to detect a successful login and save the session in your application.

Most people do it this way:

They use failwith in the child to make “sure” to remember to capture the LoginCompleted message in the parent as it will throw an exception if we don’t capture it.

However, there is a major problem, what if you add another message to capture in Login.Msg then the compiler will not be able to help us because we are using a greedy pattern | loginMsg -> .

A good solution to this problem is to use what I call ExternalMsg .

By making Login.update returns Model * Cmd<Msg> * ExternalMsg we help the compiler guide us to the correct way of handling the data. It forces me to handle ExternalMsg in the parent. Also, if I add a new case it will generate a warning telling me where I need to handle the new case.

Schedule a parent message in a child component

When working on a library or abstraction layer it can be needed to schedule a message coming from a parent in the child components.

For example Thoth.Elmish.Debouncer (we will call it debouncer from now), is using this feature in order to debounce a message.

In order to do that the debouncer exposes two functions:

  • bounce which is working withCmd<Debouncer.Msg>
  • update which is working with Cmd<Parent.Msg>

If you want to see how this is implemented, you can take a look at the code it’s only about 50 lines.

Use several views for the same component

Sometimes a component has the same features but different displays, but you still want to use it in several places in your application.
For example, in the next code we render the Switch.switch element in a different size depending of ours needs.

Extends the Elmish API

Disclaimer

In this section, I will show you advanced Elmish usage. Before using it in your application or library, you need to weight the pros and cons compare to not using the standard features set.
For example, if Cmd.ofSub works, you should always favor using Cmd.ofSub over creating a custom command.

Also, this is not intended to be a guide/tutorial on how to implement it, I just want to mention that this is possible.

Program composition

By using program composition, you can add a new behavior to any Elmish application for example you can make it support navigation via URL, render using React, attach a debugger, etc.

This is exactly what you do when wiring your Elmish application:

Custom commands

It is also possible to create your own commands. 
For example, Elmish.Browser is offering you severalNavigation commands:

Refine your user experience

You can combine all the techniques I spoke about here to improve the user exeperience of Elmish.

For example, Thoth.Elmish.Toast is a library adding global notification support for any component in an Elmish application. It uses:

  • Program composition to register the library logic and views
  • Custom commands in order to send a notification to the program directly
  • F# feature to provide a Toast builder interface using pipes

Conclusion

Elmish is a really powerful library and makes good use of the standard F# features like function composition, partial application, guides you to write safe code (handling success and error case).
It also offers you powerful customization if the standard features are not enough.

I hope you found this article useful and that it will help you. If you want another article to describe a specific subject please comments below 😊.