eyecatch

Svelteで親ComponentからのpropsのReactivityを失ってしまうパターン

Posted on 2025/12/15
# Technology

Svelteでは Component や +page.svelte の props$state(props.count) というようにすると Reactivity が失われてしまいます。

例えば以下のような let count = $state(0);Count.svelte という Component に渡します。

<!-- +page.svelte -->
<script>
	import Count from "./Count.svelte"

	let count = $state(0);
</script>

<Count {count} />

<button onclick={() => count++}>Increment</button>

<!-- Count.svelte -->
<script>
	let { count } = $props();

	let local = $state(count);
	let value = $derived(count);
</script>

<p>Local: {local} (アップデートされません)</p>
<p>Derived: {value} (アップデートされます)</p>

Count.svelte では let local = $state(count);let value = $derived(count); を定義して表示してみますが、 local の方は数字を増やすボタンを押しても画面が更新されません。

これは渡されてくる props の値を $state で囲ってしまうと deeply reactive state proxy となってしまいます。 Svelte では deep state を dependencies に反映させるには $derived を使用する必要があります。

Playgroundもあるので実際に触って体験してみてください。

SvelteKit を使用してるとよくあるミスは params をみるときです。

<!-- src/routes/[slug]/+page.svelte -->
<script>
  let { params } = $props();
  let slug = $state(params.slug); // この Slug はページを遷移しても変更されません。
</script>

{#each [1,2,3] as page}
  {#if slug === `page-${page}`}
    <p>Page {page}</p>
  {:else}
    <p><a href="/page-{page}">Page {page}</a></p>
  {/if}
{/each}

<p>{slug}<p>

上記のコードの例のようにページを遷移しても slug の変更を検知できなく、値が変わることがありません。
この場合は let slug = $derived(params.slug); を使用すると良いでしょう。

終わりに

@5.45.3 からはstate_referenced_locally の warning が発生してくれるので、気がつくことができます。
ですが warning を許容したいパターンも多いらしく、この warning を ignore する人もいるみたいです。

Table of contents
  1. 終わりに