デフォルトでは、each
ブロックの値を変更すると、ブロックの最後にDOMノードが追加および削除され、変更された値が更新されます。これは、あなたが望むものではないかもしれません。
説明するよりも、なぜそうなのかを示す方が簡単です。Thing.svelte
内では、name
は動的なプロップですが、emoji
は定数です。
「最初の要素を削除」ボタンを数回クリックして、何が起こるかを確認してください。
- 最後のコンポーネントが削除されます。
- その後、残りのDOMノードの
name
値は更新されますが、emojiは更新されません。
Reactを使ってきた場合、これは奇妙に思えるかもしれません。状態が変更されると、コンポーネント全体が再レンダリングされることに慣れているからです。Svelteは異なる動作をします。コンポーネントは一度「実行」され、その後の更新は「きめ細かい」ものになります。これにより、速度が向上し、より多くの制御が可能になります。
これを修正する1つの方法は、emoji
を$derived
値にすることです。しかし、最後のものを削除して他のものをすべて更新するよりも、最初の<Thing>
コンポーネントを完全に削除する方が理にかなっています。
そのため、each
ブロックの各繰り返しに対して一意のキーを指定します。
アプリ
{#each things as thing (thing.id)}
<Thing name={thing.name}/>
{/each}
Svelteは内部的に
Map
を使用するため、(thing.id)
の代わりに(thing)
を使用することもできます。ただし、文字列または数値を使用する方が一般的に安全です。これは、APIサーバーからの新しいデータで更新する場合など、参照の等価性ではなく同一性が維持されることを意味するためです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script>
import Thing from './Thing.svelte';
let things = $state([
{ id: 1, name: 'apple' },
{ id: 2, name: 'banana' },
{ id: 3, name: 'carrot' },
{ id: 4, name: 'doughnut' },
{ id: 5, name: 'egg' }
]);
</script>
<button onclick={() => things.shift()}>
Remove first thing
</button>
{#each things as thing}
<Thing name={thing.name} />
{/each}