Elm Data Patterns in React
Developing using React is good. React is fast, has a, reasonably, small footprint, and allows developers to use a plethora of different paradigms in their coding. Coding in Elm, is also good. Elm is fast, has a, reasonably, small footprint, and has a single paradigm in which a developer can operate. This is, in part, because Elm’s developers have been very vocal about how Elm projects should be written and how data should flow within an application. Many would agree that having options while coding is beneficial, however there is a cost to so many paradigms when switching between code bases, teams, or even individuals. An overwhelmingly popular pattern for React is the React + Redux pattern, but even this pattern has a lot of developer decisions that can be drastic when applied across an entire project, making some “React Redux” projects extremely dissimilar despite their shared data flows. Elm provides and encourages a single way for data to flow through an application thanks to it’s strict typing and architecture. There are very few ways to deviate from the core data flow because of this, making looking for the data for in an Elm app as simple as finding the update function. It follows that using a strict data flow, like Elm, on top of a well known library, like React, could yield great results — the best of both worlds.
To start applying an Elm structure to a React app, let’s use React + Redux as an example, and start by examining the function signatures for each of their “update,” and “reducer” functions.
// Redux's reduce function signature
store -> action -> store// Elm's reduce function signature (paraphrased)
store -> action -> (action, store)
The interesting difference between these two signatures is that, in Elm, actions can beget other actions. I React + Redux, orchestrating multiple actions in order is, normally, the job of a third library, such as Thunk, or Saga. If we can combine Redux and this behavior, then we can begin to cut out extra dependencies, and therefore both complexity and opportunities for developers to deviate from standards by including different libraries.
With this in mind, we can create update
functions in React that more closely resemble Elm’s:
We see that now, actions can beget other actions, so we can get a chaining effect. We can think of this as a linear way to chain actions together in a non blocking way, just like Elm.
Now, we examine the rendering function signatures.
// React + Redux view function signature
store -> html// Elm's reduce function signature (paraphrased)
store -> (html, action)
Again, the action is left out in React + Redux. This makes the binding libraries force mapping functions onto each react component. To simplify further, we can create our own wrapper function that automatically gives a handle to run any actions required. In practice, this will be similar to how Elm’s action dispatch works. In theory, the function signature will look like:
store -> (action -> null) -> html
We can write rough code for this. One thing to note is that we need the concept of a global dispatch object that will be able to propagate actions through the store. This is not dissimilar to how redux’s dispatch works.
Putting all the pieces together results in a library which is a loose wrapper around React (or Preact!) and provides a concise and opinionated way to use React in a functional way, I like to call it Elmact. Here’s an example using the final library:
You can find the whole project on GitHub: https://github.com/trezm/elmact