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.