This page looks best with JavaScript enabled

Reactivity for Arrays & Objects in Vue vs. Svelte

 ·   ·  ☕ 3 min read

Coming from the Vue world, Reactivity in Svelte for anything more than simple strings feels.. a bit different.

Vue has made me lazy when handling reactive arrays or objects.

All I have to do with the older Object API is -

1
2
3
4
// nums: [1, 2]
addToNum() {
  this.nums.push(this.nums.length + 1);
}

Directly modifying an element (e.g. this.nums[1] = 'a') would not work though, and that was perfectly fine.

It is more tricky for objects - this.planets["jupiter"] = 5 will not trigger reactivity. We could work around this by this.planets = { ...this.planets, jupiter: 5 }. (Or, use $add if you are weird).

In Composition API, this seemingly became simpler and, verbose?..

From the example in Vue docs ..

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
  <div v-for="(item, i) in list" :ref="el => { if (el) divs[i] = el }">
    {{ item }}
  </div>
</template>
<script>
  import { ref, reactive, onBeforeUpdate } from 'vue'

  export default {
    setup() {
      const list = reactive([1, 2, 3])
      const divs = ref([])
      onBeforeUpdate(() => {
        divs.value = []
      })
      return {
        list,
        divs
      }
    }
  }
</script>

Jumping back to Svelte, simple variables work as easily.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
  let clicks = 0;
  function addNumber() { 
    clicks += 1;
  }
<script>

<button on:click={addNumber}>
	Add a number
</button> {clicks}

But we cannot simply drop-in anything more complicated.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<script>
  let clicks = 0;
  let numbers = [1, 2, 3, 4];
  function addNumber() { 
    clicks += 1;
    numbers.push(numbers.length + 1); 
    // will change array, but not trigger reactivity
  }
<script>

<p>{numbers.join(' + ')} = {sum}</p>

<button on:click={addNumber}>
	Add a number
</button>
{count}

An additional statement will fix the problem -

1
2
3
4
function addNumber() {
	numbers.push(numbers.length + 1);
	numbers = numbers;
}

Or, do this -

1
	numbers = [...numbers, numbers.length + 1];

The below code also works, but I feel somewhat bad using it -

1
numbers [numbers.length] = numbers.length + 1;

Variables dependent on other variables need special treatment -

From the example in Svelte docs -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script>
	let numbers = [1, 2, 3, 4];

	function addNumber() {
		numbers.push(numbers.length + 1);
	}
	$: sum = numbers.reduce((t, n) => t + n, 0);
</script>

<p>{numbers.join(' + ')} = {sum}</p>

<button on:click={addNumber}>
	Add a number
</button>

Objects work in a similar way as arrays. Assignment to a variable on the left hand side will trigger reactivity for that variable -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<script>
let numObj = {1:1, 2:2, 3:3, 4:4};
let clicks = 0;

function addNumber() {
	let nxtNum = Object.values(numObj)[Object.keys(numObj).length-1] + 1;
  numObj [nxtNum]= nxtNum; //this!
	clicks += 1;
}
</script>

<p>
	{Object.entries(numObj)}
</p>

<button on:click={addNumber}>
	Add a number
</button>

However, the below will not work as-is -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<script>
let planet = {earth: {postn:3}};
const earth = planet.earth;

function addColor() {
	earth.color="blue";
}
</script>

<p>
{JSON.stringify(planet)}	
</p>

<button on:click={addColor}>
	Add Color
</button>

Add a line to trigger reactivity -

1
2
3
4
function addColor() {
	earth.color="blue";
	planet = planet;
}

Object API was intuitive. But, I think I prefer the Svelte solution in comparison with the Composition API in Vue.

Stay in touch!
Share on

Prashanth Krishnamurthy
WRITTEN BY
Prashanth Krishnamurthy
Technologist | Creator of Things