Reactivity, state and a unidirectional data flow
Having recently visited DotJS in Paris, I attended a talk by Evan You where he explains reactivity in frontend frameworks. This is a basic concept found in many modern frameworks, but we mostly take it for granted. Reactivity in combination with a centralised state and a unidirectional data flow is a pretty common pattern in modern frameworks. Understanding these concepts will greatly help your adoption of a new framework like, but not limited to, Angular 2, React or VueJS.
* Evan You on Reactivity at DotJS
Reactivity in frontend frameworks
When referring to reactivity Evan means that you see your UI change immediately when you change some value somewhere, this value could be inside a component or in your main application state. There are many strategies to achieve this, Evan makes the distinction between two; pull-based and push-based change propagation.
With pull-based change propagation we need to have a handler which get’s called whenever a state change takes place. We can then re-render the entire component tree or we can render from a specific component and down. In the case that not all children need to be updated we can use methods like shouldComponentUpdate (React) or changeDetectionStrategy.onPush (Angular2). The basic premise is that the change in state is ‘pulled’ into the component and it’s children.
The gist of pull-based propagation
On the other hand there is a push-based change propagation strategy. This method assumes each property in your state exposes getters and setters. This allows the framework to perform dependency tracking in which each piece of state has it’s own subscribers. When something changes in the state, these subscribers are notified and they can update themselves, so they get the new state pushed instead of having to wait for a signal to check if they need an update.
The gist of push-based propagation
VueJS 2 uses a push-based change propagation strategy which increases the efficiency of state change propagation and also implements the ‘shouldComponentUpdate’ automatically so you don’t have to do this manually anymore.
Application state and a unidirectional data flow
Another concept often found in modern frontend frameworks is a centralised state in combination with a unidirectional data flow. A centralised state is also a great tool when looking at a component tree and how it updates. Basically each component is a pure function which receives state and returns a piece of DOM for instance.
To make sure the view (all components) is only rendering when it needs to, we should optimise the updates when state changes. If we would let components update their own internal state or communicate with other components directly we wouldn’t be able to update the view solely based on application state. We would be introducing a lot more variables which makes it much harder to predict when we should update and when we can skip a certain part. It also creates dependencies between components which reduces their interchangeability.
By using actions which are triggered from a component we create a unidirectional data flow. In short, data flows in one way only;
* State determines the view.
* Components inside the view trigger actions.
* Actions update the state.
* The new state is rendered into the view.
* Courtesy of Vuex
A unidirectional data flow also makes your application very well maintainable. All components are independent, they only have input and generate output. By looking at the actions it’s very clear to see what can happen inside your application. Also your centralised state exposes (almost) everything your application knows about.
Sometimes it’s fine to have some local state inside a component existing alongside a centralised application state. It’s mainly personal preference and there’s quite some debate about it, but I would suggest to only use local state for something that’s confined to that component, as soon as it has impact on or should be derived from other components it’s better to centralise it.
Reactivity in combination with centralised state and a unidirectional data flow can be done in Angular 2, React and VueJS. Of course many other frameworks also share similar traits in one way or another, but the main take-away here is that focussing on shared concepts will give you a better understanding of the problem each framework is trying to solve for you, and how it might help you.
If it feels a bit overwhelming to get started with a large framework it might be better to spend some time on these concepts at first. I personally really enjoyed working with ChooJS. It’s a tiny framework which utilises a unidirectional data flow, a central state and reactivity on state changes. It doesn’t have a lot out of the box, but that also makes it easy to grasp. Go through the getting started guides, try to build something and you’ll run into issues which are all covered in the larger frameworks, but at least you’ll understand why you need them and which implementation you like best.
Another interesting take on the unidirectional approach is CycleJS. This is a functional and reactive framework which works entirely with streams. It’s basically one big pure function which takes input (button clicks etc.) and it’s output can affect the DOM, or any other side-effect you want it to perform.