The Elm Architecture in JavaScript

These days I’m working on a project named elm-architecture, which is The Elm Architecture implemented in JavaScript.

Background

I used to use Redux. Redux is good, but after using it in my daily work for half year, I do think it is just an ultimate Flux implementation, it doesn’t make code better or cleaner. And that’s why I started my elm-architecture project.

Implement it in the right way

The Elm Architecture is the architecture for Elm language. It relies on language features a lot, and that makes the code very clean.

I want to keep the taste as close as possible, trying hard not to add helper functions or magic properties. This is what I’ve made so far

The Counter example. Elm (left) vs JavaScript (right)

Compare The Elm Architecture to Redux

Something happened: Messages vs Actions

These are how The Elm Architecture and Redux describe “Something happened”, like events, you handle them based on their type.

In Redux, an action is a plain object, it’s type is a property.

{ type: ‘INCREMENT’ }
{ type: ‘DECREMENT’ }

And this is how you handle them

switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
}

However, messages in The Elm Architecture is a function.

function Increment() {}
function Decrement() {}

And this is how you handle them

switch (msg.constructor) {
case Increment:
return model + 1
case Decrement:
return model - 1
}

There are bunch of benefits of messages as functions

  1. The type of messages are NOT String. You will never misspell a message type. Even if you did, browsers will throw an Error to you immediately. Say goodbye to the huge action type constant list.
  2. No need to write message creators. Want to create a message? just call new Increment() and you get one. In Redux, you have to write action creators every time, so you get 'INCREMENT' as action type and function Increment() as it’s creator, double code to write.
  3. Messages are created automatically. Most of the time The Elm Architecture will create messages automatically, you don’t need to write new Increment()yourself, just pass Increment to handlers, let them generate messages for you.
  4. Typed language support. Since messages are instances, when using language like TypeScript, it will give you great hints and code assistance.

Side Effects: Cmd/Sub vs Middleware

Middleware in Redux can be a mess. As a middleware user I have to make sure they applied in proper order, which one should I apply first? Are they going to conflict to each other? As a middleware author I have to think, Redux-Thunk intercepts functions, so I can’t make a middleware intercepts functions too. Also I have to remember to pass actions I don’t know to next middleware, why should I take care the action sending flow? If someone made a mistake, actions will just gone. So much considerations.

The Elm Architecture gives you a simple way to do side effects — cmd/sub. Unlike middleware of Redux, it’s not chain-able, each side effect manager only take care of it’s job.

State Management: Model/Update vs Reducers

Both Redux and The Elm Architecture take single central state approach. The difference is Redux slice state into several reducers, and The Elm Architecture only have one model with an update function (You can think it as a single reducer).

The good part of reducers is the scopes are small, you can handle them piece by piece, ideally you can even reuse them across your apps. The bad part is, you have to spend time to decide how to divide your state, if you doing it wrong, you may face scaling problems. Also you will encounter questions like How do I share state between two reducers?

The Elm Architecture is just simple, only one single model, no hesitation at all.

Connect to view: Call view function vs Provider/Connect

Redux encourages users wrap components with Connect component when needed. The main concept is to decouple View and State, this goal sounds great, but the price is also very high, you write more and more code such as mapStateToProps, mapDispatchToProps, Provider and Connect to achieve it, is it really worth it?

In The Elm Architecture, you pass model to view function to render your view. As the result view is coupled with Model. That sounds bad. But this is the idea of The Elm Architecture — no components, only view pieces.

We do not think in terms of reusable components. Instead, we focus on reusable functions…we create reusable views by breaking out helper functions to display our data.
Scaling The Elm Architecture

Which one is better? It depends on your needs. To me, I prefer to write less code, getting things done quickly.

Summary

Elm is a young language, The Elm Architecture is young as well. It has great potential, you can’t talk about modern web development without mention it. Maybe someday I’ll not able to implement it in pure JavaScript with such clean structure, who knows?

If you’re interested in using The Elm Architecture in JavaScript, or you’ve had enough writing boilerplate in Redux, elm-architecture is here, any contribution or feedback is welcome.