Overview
Reactivity is a way to automatically update the system based on changes in data flow.
A data stream is any sequence of events from any source, ordered in time. For example, in a keylogger, an application that records keystrokes on a keyboard, the data stream would be the keystroke signals.
Reactive programming - a paradigm in programming in which the program focuses more on controlling data flows, thus describing the relationships between them.
What is reactivity?
Let's remember how a user imagines working with an interface. We expect the interface to react instantly to our actions. We start typing text, it immediately appears in the field. Clicked a button, the form immediately changes, loaders appear, and then a happy success message jumps out. From the user's point of view, responsiveness is an immediate reaction to their actions.
For a developer, the term has a slightly different meaning. When it comes to reactivity, the focus shifts to the data. To give an example, let's write code to add two numbers together:
let a = 3
let b = 4
const sum = a + b
console.log(sum) // 7
The sum variable now stores the sum
of the numbers, and we know that it will never change. If we change the values of variables a
and b
after addition, it will not affect the result:
// ...continuation of the previous example
// Changing the values of the summands
a = 10
b = 8
console.log(sum) // Still 7
To get a new sum, we need to add these numbers again. It seems logical, but what if these numbers are entered by the user and they may change? In this case it would be more convenient if the programme could update the value by itself.
// ☝️ it's pseudo-code, real code doesn't work like that!
let $a = 3
let $b = 4
const $sum = a + b
console.log($sum) // 7
$a = 2
$b = 9
console.log($sum) // 11
$a = -4
console.log($sum) // 5
The example above isn't real, it won't work that way, but it gives an insight into the kind of problem reactivity can solve.
In the frontend, we can link any change in the interface to changes in the data within the programme. Reactivity in terms of frameworks is about updating the interface based on state changes.
In reactivity, data is often represented as a stream - a sequence of events ordered by time.
The data stream can start processing at any point in time, and all new events can be processed. Reactivity is thus a convenient way to synchronise data with the interface the user sees, for example. Reactivity helps to focus on the data as it is related to each other, which is much closer to business logic.
Types of reactivity
The basic idea of reactivity builds on the Observer pattern. This is a behavioural design pattern that creates a subscription mechanism that allows others to follow and react to events that occur at the source. At what point subscribers learn about updates depends on the type of reactivity. There are two types of reactivity: push and pull.
Push Reactivity
When a change occurs in a push reactive system, it pushes the change to all subscribers on its own. With this type of reactivity, all subscribers will receive the actual change as soon as it happens.
If we subscribe to an event (like a click), the browser will immediately notify all subscribers when it happens.
document.addEventListener('click', () => {
console.log('I reacted to a click!')
})
In the JavaScript ecosystem, the most popular way to use push reactivity is to use the RxJs library.
// Creating a source:
const $clicks = Rx.Observable.fromEvent(document, 'click')
$clicks.subscribe(event => {
console.log('I reacted to the click reactively!')
})
A disadvantage of push reactivity can be repetitive computations, since when data changes, all subscribers can redo their work on that data. This can be a problem with frequent updates, when the system can be simply swamped with a large data stream and all subscribers are endlessly doing calculations.
Suppose there is a dashboard application that displays up-to-date information with graphs and changes. The page may consist of many different blocks, and each one needs information. When data comes in continuously, the entire page will be constantly updated in different places. Some calculations may be more complex and longer than others, causing new data to pile up in the queue that needs to be calculated.
In JavaScript code that uses libraries for reactivity, it is common to see variables labelled with a
$
dollar sign, as in the example above. In this way, a variable is given a special prefix, indicating that it contains a stream and can be subscribed to. A second option is to add anS
at the end:clicksS = Rx.Observable.fromEvent(...)
.
Pull Reactivity
Pull reactivity works in the opposite way to push reactivity. Calculations caused by changes in the source data are postponed here until they are needed. With this type of reactivity, subscribers will pull new data only when the entire system is updated.
A real life example would be the pull-to-refresh pattern from mobile apps. For example, on Twitter, a user can pull content from the top edge down a bit and then release it. This action will cause the feed of tweets to refresh and the user can see the actual data. With the push approach, the feed would refresh itself every time a new tweet appears.
Pull-mechanics are rarely found in libraries, because they are often only partially present in some places, such as MobX.
The downside of the pull approach is performance issues - it can be costly to update the entire system each time to notify subscribers of new data.
Reactive Programming
The ideas of reactivity eventually led to a new paradigm that is based on asynchronous control of data flows. While it was previously stated that event subscriptions in the browser are also a reactive model, reactive programming elevates these ideas to an absolute. This means that data streams can be created from anything and managed however you want: crossing, transforming, filtering, and so on. For example, in an application written using the MVC approach, reactive programming can be used to create automatic reflection of changes from Model to View, and vice versa, from View to Model.
Reactive programming helps to abstract away from describing actions in code directly and focus on data relationships. It is designed to make it easier to create programmes with a large number of relationships.
In modern frontend, you can find complex client applications where changing values in fields leads to a number of interface updates. Reactive programming can make it easier to create such systems. There is even an entire Reactive Systems manifesto describing how a reactive system should behave.
Reactive programming itself is rarely found in pure form. It is often combined with other paradigms. This is how such mixes as Imperative Reactive Programming, Object-Oriented Reactive Programming and Functional Reactive Programming appeared. The latter is the most popular, and the Elm language is considered one of its main representatives.
Support ❤️
It took a lot of time and effort to create this material. If you found this article useful or interesting, please support my work with a small donation. It will help me to continue sharing my knowledge and ideas.
Make a contribution or Subscription to the author's content: Buy me a Coffee, Patreon, PayPal.