點燈坊

失くすものさえない今が強くなるチャンスよ

Svelte 之 Reactive Array

Sam Xiao's Avatar 2019-11-26

若要使 Array 也能 Reactive,Svelte 有些限制,必須改變傳統 ECMAScript 寫法,改用 ECMAScript 2015 方式。

Version

macOS Catalina 10.15.1
WebStorm 2019.2.4
Svelte 3.0.0

Reactive Array

<script>
let numbers = []

let onClick = () => numbers.push(1)

$: sum = numbers.reduce((a, x) => a + x, 0)
</script>

<button on:click={ onClick }>Add 1</button>
{ sum }

10 行

<button on:click={ onClick }>Add 1</button>
{ sum }

每次 button 按下都會增加 1,並即時顯示累加結果。

第 2 行

let numbers = []

let onClick = () => numbers.push(1)

每次 button 按下都會對 numbers array 增加 1,很直覺使用了 push()

第 6 行

$: sum = numbers.reduce((a, x) => a + x, 0)

因為希望能即時顯示累加結果,因此宣告了 sum,對 numbers 使用了 reduce() 計算累加結果。

但實際執行會發現 sum 並沒有如預期自動累加。

主要原因是 Svelte 的 reactive array 來自於 = 對 array 的 assignemnt,push() 是直接對 element 新增,因此 numbers 無法成為 reactive array。

<script>
let numbers = []

let onClick = () => {
  numbers.push(1)
  numbers = numbers
}

$: sum = numbers.reduce((a, x) => a + x, 0)
</script>

<button on:click={ onClick }>Add 1</button>
{ sum }

第 4 行

let onClick = () => {
  numbers.push(1)
  numbers = numbers
}

補上 numbers = numbers,因為有 =numbers 就成為 reactive array 了。

numbers = numbers 寫法很蠢,且很多 lint 都會 complain

<script>
let numbers = []

let onClick = () => numbers = [...numbers, 1]

$: sum = numbers.reduce((a, x) => a + x, 0)
</script>

<button on:click={ onClick }>Add 1</button>
{ sum }

第 4 行

let onClick = () => numbers = [...numbers, 1]

Svelte 官方建議改用 ES6 的 array spread 寫法,如此因為有 =,則 numbers 就是 reactive array。

同理如 pop()shift()unshift()splice() 都要改用 ES6 寫法。

array000

Conclusion

  • 若以 FP 角度,push() 等都是以 side effect 改變 array,而 ES6 的 array spread 則以 immutable 改變 array,雖然為了 reactive array 要改變 ECMAScript 寫法,但事實上也是比較好的 practice

Reference

Svelte, Reactivity / Updating arrays and objects