Introduction
Most of the articles on the features of view libraries talk about the “what” part of it such as its syntax, learning curve, and best practices from a development perspective.
This article is not going to fall under the same category instead, it explains one of the key expectations of a declarative language or view library which is Reactivity.
Let us see “by how” reactivity is achieved in popular view libraries Vue and React.
Though from the usage point of view, it may look like each JS framework has its way of feature implementation for the Single Page Application approach, behind the scenes all such features are based on Vanilla JS and its experimental features.
Let’s dive into the implementation details to know how the JS features are elevated to fulfill the Reactivity mechanism.
Why Reactivity and why not any other core concepts?
The principles behind other core building blocks of React and Vue like Templating, Composition of components, DOM manipulation strategies (using Virtual DOM) are more or less similar.
Supporting blocks like Routing, State Management are also not focused on this article since those are either provided as secondary support from the library or a 3rd party library is used for that purpose as a de facto standard.
Reactivity
In general, Reactivity is a mechanism that maintains the sync between 2 related layers. Typically, this is achieved by watching for changes, emitting the events on mutation, and running the sync operations.
In web development, Reactivity is how the state and its behavior (Model) sync with the DOM (View) during mutations.
JavaScript is not reactive (at least not available as a built-in feature) i.e., when the state gets a new value, the logic and the view depends on it won't get recomputed automatically. It's the responsibility of the framework and/or developer to make sure both model and view are in sync.
Reactivity in Vue
VueJS uses the native Proxy API (available from ES6) to hook into the calls on data objects for adding custom behavior.
Let’s first take a glance at how this Proxy API works.
Proxy API
It is simply a wrapper around JS object where it can receive messages like target object, provided it has support to trap the operations over the object properties via the handler.
Without a handler, sending messages to proxy objects behaves identically to the original object.
As depicted in the above diagram, a handler can hook into the operations over the target properties such as read, write, etc.
We can add the tracking mechanism by intercepting the getter and setter functions via proxy handlers.
“get” function inside the handler would be called on while “reading” a property
“set” would be called on “write” action on the property
In Vue, the goal of reactivity is collecting the dependencies list (model) for every side effect (a function that uses the data model) and to recall all the side effects when the respective data gets mutated.
Handler: get (Track) | Handler: set (Trigger) | UI |
---|---|---|
A place to add the property to the dependency list against the current effect (a function that read this property) | A place to re-rerun all the effects that use the updated property | Templates are maintained as nodes in Vue so it can also be treated as a data model, and it is preserved as an effect. This will be re-rendered if any of its properties gets mutated. |
Reactivity in React
React doesn’t have any inbuilt mechanism for COMPLETE reactivity in action such as observe/track the changes in the data model and thereby updating the behavior and view itself. So, we can say React is not Reactive!
The developer should explicitly call state mutation calls to make it sync with the view. If any side effects need to be run, that must be wrapped in useEffect hook. All the side effects get a chance to run on whenever the component renders. Side effects with any dependency will run only if the dependency has any new value compared to the previous one.
All state update requests within an effect, event handlers are batched, and React may update it in a single go asynchronously.
Let’s see this through an example
As you can see, the data model can’t be mutated directly. i.e., this statement is not effective in React // someState = newValue
Either setState (for class components) -or- state update functions from useState/useReducer hook must be used to submit the update request to React. If there are any dependent computations, that should be wrapped as a side effect.
Summary
To summarize, let us define the type of reactivity in Vue and React.
Push
In Vue, the library itself takes care of tracking the dependencies and keeps the model and view in sync. It follows the track and trigger mechanism. This type is being called “PUSH” because we are not explicitly data tracking and forcing the re-rendering on data mutation. When there is a mutation, respective view changes are pushed automatically.
Pull
Technically, the state update calls are requests to React. Such request calls are scheduled, and they may not happen immediately. The control is with React and it will decide when to PULL the changes to update the DOM. Keeping the rendering control with itself, React can defer, prioritize, batch up the state update requests for better performance.
Talk to us for more insights
What more? Your business success story is right next here. We're just a ping away. Let's get connected.