若要使 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 寫法。
Conclusion
- 若以 FP 角度,
push()
等都是以 side effect 改變 array,而 ES6 的 array spread 則以 immutable 改變 array,雖然為了 reactive array 要改變 ECMAScript 寫法,但事實上也是比較好的 practice