Table of contents
1.
Introduction
2.
Reactivity
3.
How Vue Knows What Code Is Running
4.
How Vue Tracks These Changes
5.
Proxied Objects
6.
How Rendering Reacts to Changes
7.
FAQs
8.
Key takeaways
Last Updated: Mar 27, 2024

Reactivity in Depth

Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

We often come across the word Reactivity in web development. What does it mean? When we write a code and print the output, the output doesn't automatically update itself when the front-end values are altered. A system that automatically adapts to the changes made to the variables and functions by the user is called Reactive.

Technically speaking, Reactivity in a framework is a declarative programming model that takes care of keeping the DOM(Document Object Model) in sync with the updates to the current state

Let's understand Reactivity further!!

Reactivity

Reactivity is a programming model which enables us to adapt to changes in a declarative method.

Let us understand Reactivity with an example:

When we write a formula for summation in an Excel sheet, the result automatically changes by altering the values in the concerned cells.

In the above picture, the current values and the sum are 4, 5, and 9. But even if we change the value of A2 to 6, the sum updates itself.

As we can see, the value in C2 got automatically updated when the value in cell A2 was changed to 6.

This is called Reactivity.

But this is not how Javascript works. A typical JS code for addition would look like this:

let num1 = 2
let num2= 3
let sum = num1+ num2


console.log(sum) // 5


num1= 3


console.log(sum) // Still 5
You can also try this code with Online Javascript Compiler
Run Code

 

Even if the values in variables num2 and num2 are changed, the result is not updated.

So, how do we do it?

We need to think of the following three steps:

  1. Track when a value is read. e.g. num1+ num2 reads both num1 and num2.
  2. Detect when a value changes. e.g., When we assign num1= 3.
  3. Re-run the function that read the value originally. e.g. run sum = num1+ num2 again to update the value of sum.

Let's see how Vue.js implements the steps mentioned earlier.

 

How Vue Knows What Code Is Running

To be able to perform our sum anytime the numbers vary, our sum statement needs to be wrapped in a function:

const updateSum = () => {
  sum = num1+ num2
}
You can also try this code with Online Javascript Compiler
Run Code

 

Vue keeps track of which function is presently running by using an effect. An effect is a wrapper around the function that commences tracking right before the function is called. Vue knows which effect is executing at any given instant and can re-run it when appropriate.

Let's try to create something like the effect wrapper but without Vue.

For this, we’ll need something to wrap around our summation:

createEffect(() => {
  sum = val1 + val2
})
You can also try this code with Online Javascript Compiler
Run Code

 

We may implement the above effect something like this:

// Maintain a stack of running effects
const runningEffects = []


const createEffect = fn => {
  // Wrap the passed fn in an effect function
  const effect = () => {
    runningEffects.push(effect)
    fn()
    runningEffects.pop()
  }


  // Automatically run the effect immediately
  effect()
}
You can also try this code with Online Javascript Compiler
Run Code

 

When our effect is called, it puts itself onto the runningEffects array before invoking fn. Anything that needs to know which effect is currently running can check that array.

Effects act as the starting point for many crucial aspects. For example, both component rendering and calculated properties employ effects internally. Any time something magically responds to data changes, you can be quite sure it has been wrapped in an effect.

While Vue's public API doesn't contain any means to create an effect directly, it does expose a function called watchEffect that performs a lot like the createEffect function from our example.

 

How Vue Tracks These Changes

We can not track reassignments of local variables like those in our earlier instances, because there's just no method for accomplishing that with JavaScript. But we can track changes to object properties.

When we return a raw JavaScript object from a component's data function, Vue will encapsulate that object in a Proxy with handlers for get and set. 

A proxy is an object that encases another object and allows you to intercept any interactions with that item.

We use it like this: 

new Proxy (target, handler)
You can also try this code with Online Javascript Compiler
Run Code
const dinner = {
  meal: 'tacos'
}


const handler = {
  get(target, property) {
    console.log('intercepted!')
    return target[property]
  }
}


const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)


// intercepted!
// tacos
You can also try this code with Online Javascript Compiler
Run Code

 

Here we've intercepted attempts to read the properties of the target object. A handler function like this is sometimes known as a trap. There are numerous various types of traps available, each managing another kind of interaction.

One problem with using a Proxy is this binding. We want any methods to be tied to the Proxy rather than the target object so that we may intercept them also. Thankfully, ES6 brings another new feature, called Reflect, that allows us to make this problem disappear with no effort:

const dinner = {
  meal: 'tacos'
}


const handler = {
  get(target, property, receiver) {
    return Reflect.get(...arguments)
  }
}


const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)


// tacos
You can also try this code with Online Javascript Compiler
Run Code

 

The first step towards developing Reactivity with a Proxy is to track when a property/variable is read. We do this in a function called track where we pass in the target and property, in handler: 

const dinner = {
  meal: 'tacos'
}


const handler = {
  get(target, property, receiver) {
    track(target, property)
    return Reflect.get(...arguments)
  }
}


const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)


// tacos
You can also try this code with Online Javascript Compiler
Run Code

 

The implementation of the track isn't displayed here. It will check which effect is presently executing and store that alongside the target and property. This way Vue knows that the property depends on the impact.

Lastly, we need to re-run the effect when the property value changes. For this, we're going to require a set handler on our Proxy:

const dinner = {
  meal: 'tacos'
}


const handler = {
  get(target, property, receiver) {
    track(target, property)
    return Reflect.get(...arguments)
  },
  set(target, property, value, receiver) {
    trigger(target, property)
    return Reflect.set(...arguments)
  }
}


const proxy = new Proxy(dinner, handler)
console.log(proxy.meal)


// tacos
You can also try this code with Online Javascript Compiler
Run Code

 

The proxied object is not visible to the user, but under the hood, it enables Vue to execute tracking dependencies and changes-notification when properties are accessed or modified. 

If we want to rewrite our original example using a component we may do it as follows:

const vm = createApp({
  data() {
    return {
      num1: 2,
      num2: 3
    }
  },
  computed: {
    sum() {
      return this.num1+ this.num2
    }
  }
}).mount('#app')


console.log(vm.sum) // 5


vm.num1= 3


console.log(vm.sum) // 6
You can also try this code with Online Javascript Compiler
Run Code

 

The object that data returns will be wrapped in a reactive proxy and stored as this.$data. The properties this.num1 and this.num2 are aliases for this.$data.num1 and this.$data.num2 respectively, so they go through the same Proxy.

Vue will wrap the function for the sum in an effect. When we try to access this.sum, the efect will be executed to calculate the value. The reactive Proxy around $data will track that the properties num1 and num2 were read while that effect runs.

 

Proxied Objects

Vue keeps track of all reactive objects internally, so it always returns the same proxy for the same object.

When a reactive proxy accesses a nested object, it converts the object to a proxy before returning it:

const handler = {
  get(target, property, receiver) {
    track(target, property)
    const value = Reflect.get(...arguments)
    if (isObject(value)) {
      // Wrap the nested object in its own reactive proxy
      return reactive(value)
    } else {
      return value
    }
  }
  // ...
}
You can also try this code with Online Javascript Compiler
Run Code

How Rendering Reacts to Changes

A render function is created by compiling a component's template. The render function creates the VNodes that describe how the component should be drawn. It's wrapped in an effect, which allows Vue to keep track of which properties are 'touched' while it's running.

A calculated property is essentially comparable to a render function. Vue doesn't keep track of how dependencies are utilized; all it knows is that they were used at some time during the function's execution. If any of those properties change in the future, the effect will re-run, triggering the render function to build new VNodes. These are then used to make the required DOM changes.

FAQs

  1. What is Reactivity in Vue?
    The Vue JavaScript framework is "reactive," which means it can automatically refresh your data where changes are made. Data must be rendered dynamically in various HTML elements in modern online applications.
     
  2. How does Vue reactivity work?
    The reactive method in Vue allows you to construct a reactive state in JavaScript or Vue. The reactive method is essentially just a function that constructs a proxy and wraps it over specified data objects, resulting in a proxied object.
     
  3. Are Vue methods reactive?
    No, methods are not reactive. Only data can be reactive in Vue. It doesn't matter if you reference the data member directly, use it in computation or in a method. 

Key takeaways

Reactivity is a model which helps in updating values of properties when the assigned values are altered. Vue uses Proxies to track the functions and achieve reactivity.
Check out this problem - Subarray Sum Divisible By K

Coding Ninjas brings to you a wide range of interesting blogs. To know more about web technologies please visit, Coding Ninjas Web Blogs.

Happy learning!!

Live masterclass