Skip to main content

デフォルトでは、each ブロックの値を変更すると、ブロックの 末尾 に DOM ノードが追加・削除され、変更された値が更新されます。これはあなたが期待した動作ではないかもしれません。

説明するより見ていただいたほうがおわかり頂けるでしょう。Thing.svelte の中を見てみると、name は動的な prop ですが emoji は定数です。

‘Remove first thing’ ボタンを何回かクリックすると、以下のことが発生します:

  1. 一番最後のコンポーネントが削除される
  2. 残りの DOM ノードの name の値が更新されるが、絵文字は更新されない

もしあなたが React を経験しているなら、奇妙に見えるかもしれません。なぜなら、あなたは state が変更されたらコンポーネント全体が再レンダリングされることに慣れているからです。Svelte の動作は異なります: コンポーネントは一度だけ実行され、その後の更新は ‘きめ細やかに(fine-grained)’ 行われます。これにより、より高速に、よりコントロールしやすくなります。

これを修正する方法の1つとしては、emoji$derived の値にすることが考えられます。しかし、末尾の <Thing> コンポーネントを削除して全てを更新するより、先頭の <Thing> コンポーネントを完全に削除するほうが、理にかなっています。

そのためには、each ブロックの each イテレーションに一意な key を指定します。

App
{#each things as thing (thing.id)}
	<Thing name={thing.name}/>
{/each}

Svelte は内部的に Map を使用しているので、どんなオブジェクトでもキーとして使用できます。つまり (thing.id) の代わりに (thing) を使うことができます。しかし、文字列または数値を使用する方が一般的に安全です。なぜなら、例えばAPIサーバーからの新しいデータで更新する場合に、参照が等しくなくても同一性が持続することを意味するからです。

Edit this page on GitHub

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}