This article was first published on Pragmatic Coder by Aurelia core team member, Vildan Softic. If you enjoy this, why not head over there and check out some of his other work.
We can’t ignore the ever-growing importance of changing paradigms, no matter whether we’re talking about social, political or software development matters. Of late, front-end web development has seen an increasing interest in predictable state containers, introduced by concepts such as Flux and made popular by Redux. Simultaneously, the trend towards a more functional style of programming — particularly component composition — has changed the way we think about building applications.
At first glance, neither idea may appear that important or world changing in its own right, but put together they can provide a great developer experience. I’m not going to judge whether this is a better approach compared to well-known concepts such as MVVM and classic services. Rather I’d like to share an approach which helps you to combine both concepts, so as to get the best of both worlds.
This article talks about the theory, actual code and a full example can be found over at GitHub. Sources, including templates, are fully commented to explain design choices and the repo’s README contains a recommended way to review the example. As such we won’t waste time on implementation details, like the use of RxJS, but get straight to understanding the core concepts.
A Modern Development Approach
A modern development approach leverages a single store, which acts as a fundamental basis for your application. The idea is that this store holds all the data that makes up your application. The content of your store is your application’s state — a snapshot of your application’s data at a specific moment in time. In functional terms, if we were to represent our whole application with a single function
renderApp, the state would be the argument we pass in.
function renderApp(state): RenderedApplication
If we only wanted to produce static sites without any interaction, we’d already be good and could stop work here. However, most of today’s apps provide a plethora of interactions. So if the state is a snapshot at a specific point in time, an event can be seen as the trigger that changes our state from current to new. Such a user interaction can be compared to a reducer, which modifies the current state by applying instructions from a certain action.
function userInteraction(oldState, ...actionInstructions): NewState
Modification though, is a dangerous game. If we change the original source, how will we know the difference between the new and old state? As such immutability is a key aspect of modern approaches, as it maintains the original source and creates a modified copy of your new state. So the current state becomes the old state and the interaction creates the next current state.
CURRENT STATE --> USER INTERACTION --> NEW STATE renderApp(currentState) --> userInteraction(currentState, ...) --> renderApp(newState)
Past, current and the future are snapshots of state after a given amount of actions. Keeping this in mind we can move the current state backwards, by reversing actions and traveling back to a previous state.
NEW (aka CURRENT STATE) --> USER INTERACTION * -1 --> CURRENT (aka OLD STATE) renderApp(newState) --> userInteraction(newState, ...) --> renderApp(currentState)
The interesting point here is that the functional call sequence does not change — only their inputs do. As such we can conclude that a state is solely influenced by actions and that given a specific input, we can always expect the same output. This reflects the nature of pure components.
Continue reading %Managing State in Aurelia with Higher Order Components%