State Management with Phoenix LiveView and LiveEx

Peter Ullrich
Jul 23, 2019 · 6 min read

At grandcentrix, we use the Elixir + Phoenix stack to deliver scalable, fault-tolerant and high-availability IoT solutions to our customers. The web frontends, we build for managing these solutions, need to be developed rapidly and adjusted constantly to the needs of our customers. The Phoenix framework is a perfect choice for our needs, but unfortunately requires us to add interactivity using JavaScript. That changed when the Phoenix LiveView project came along.

With Phoenix LiveView, adding interactivity and displaying state changes without reloading the page becomes trivial. The complexity and loss of productivity due to context switching decreases significantly by eliminating the split between the backend and frontend code. But now that the normally stateless, since static, frontend becomes stateful, a new question arises: How can we manage the state?

The Problem

Once a stateful frontend grows in complexity, it becomes significantly more difficult to keep oversight of how the state is changed, when, by whom, and in what order. Think about state changes caused by asynchronous calls that fetch data from the backend, a websocket connection that pushes new updates to the data, and on top of all of this, have the user edit the state as well. All these actors change the state and since they usually do this directly, it quickly becomes unclear what is happening.

The Solution

Facebook ran into these issues as their website grew in complexity and proposed an architectural pattern they called flux. If you want to understand this pattern in-depth, we recommend Facebook’s introduction to the topic. In high level terms, the flux pattern wraps every state change into an action with a type and a payload. Every action has to go through a dispatcher, which is similar to a funnel, through which every action has to go sequentially. The dispatcher applies the change to the state using callbacks (sometimes called mutations) which were previously registered by a store to handle certain actions. The stores emit a change event if their state has changed. Special views called controller-views receive that event, fetch the latest state, and apply the updates to their views.

The advantage of the flux pattern is that state changes become observable and replicable. It helps tremendously in debugging breaking state changes and monitoring the information flows of larger frontend applications. It has proven itself to be so useful in sanitizing state changes that every larger single-page application (SPA) framework has an implementation of the flux pattern like Vuex, Redux, and NgRx. In our experiences with LiveView, we ran into the exact issues that these libraries solve and therefore we decided to build a flux implementation for Phoenix LiveView, called LiveEx.

LiveView meets Flux

In order to facilitate the use of the flux pattern in LiveView, we created the LiveEx library, which helps set up the flux architecture. LiveEx is inspired by the Vuex library, thus it adheres to the following flux flow:

Vuex flux flow. LiveView dispatches Actions, which commit Mutations that mutate the Store which updates the LiveViews
Vuex flux flow. LiveView dispatches Actions, which commit Mutations that mutate the Store which updates the LiveViews
Flux Flow of the Vuex library

The Vuex flow deviates a bit from the original flux flow since it allows side-effects (e.g. asynchronous or database calls) in its actions. However, everything after the actions must be side-effect free and synchronous. In short, a view dispatches an action, which does its logic and eventually commits a mutation. The mutation actually changes/mutates the state and stores the updated state back in a store. The store notifies the views about its state change, which pull the latest version of the state and update themselves.

LiveEx gives you all the tools to write your own store with actions and mutations and lets you dispatch actions easily from within any LiveView that is connected to a particular store. Subsequently, we show how to get started with the LiveEx library.

Setup LiveEx

We created an example project that implements LiveEx and open-sourced it on GitHub. Below, we explain how to set up your own project.

Add the LiveEx library to your Phoenix project:

Create a module for your store. A fully documented version of an example store can be found on GitHub. Here an example for how it could look like:

Let’s discuss this. First, store variables can also be added dynamically, but it is a good idea to write out the entire structure of your store once. Thus, define every store variable that you will use in your application with a default value in the initial_store map. Pass this map to the init/2 function together with a LiveView.Socket. This will initialize your store.

Below your init/1 function, define your actions. Use pattern-matching to match the appropriate action with an action type. Perform any business logic in the action, potentially with side-effects or asynchronous calls. Eventually, commit a mutation that updates the state.

Mutations have to be free of side-effects and typically contain little to none business logic except for what is necessary to update the state. Please note that their function name needs to equal the name with which they should be committed (in this case set_a).

Next, connect your store with your “root” LiveView, which is the LiveView that encapsulates any LiveViews that should use the same state.

Make sure to pass any state variables down to the nested LiveViews with

Within every nested LiveView, initialize the store as well in the mount/2 function with the passed-on state variables (here: [:a, :b, :c, :d])

Use LiveEx

Display your state in your .leex templates by using the common Phoenix Template syntax:

To change your state, simply call the dispatch/3 function with the type, (optional) payload, and LiveView.Socket and let LiveEx do its magic.

The dispatch/3 call will create and commit an action, change the state, and update the state of your LiveView.Socket automagically!

Debugging

Now that we send every state change through the dispatcher, it becomes trivial to log the state changes. Hence, if you set your Logger to debug, you will see how your actions are changing the state. Here is an example from the console:

Conclusion

LiveEx will hopefully help you to develop middle to larger frontends. It is not yet battle-tested, nor optimized. It will change dramatically once the LiveView team implements support for prepending/appending data since then each store state can be kept in a dedicated socket assignment. More on this here.

If you want to help to make state management with LiveView easier, your help and feedback are most welcome and best handed in on the LiveEx GitHub page.

For further information, please check out the LiveEx Example Repository.

Otherwise, if you’re interested in developing IoT solutions with Elixir, check out our jobs page.

grandcentrix

thoughts, stories and ideas | from the grandcentrix team |…

Peter Ullrich

Written by

Peter ist ein sportbegeisterter Elixir-Entwickler bei Studitemps, radfahrender Afrikaliebhaber und leidenschaftlicher Teiler seines Wissens.

grandcentrix

thoughts, stories and ideas | from the grandcentrix team | https://www.grandcentrix.net

Peter Ullrich

Written by

Peter ist ein sportbegeisterter Elixir-Entwickler bei Studitemps, radfahrender Afrikaliebhaber und leidenschaftlicher Teiler seines Wissens.

grandcentrix

thoughts, stories and ideas | from the grandcentrix team | https://www.grandcentrix.net

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store