Skip to main content

$effect

effect は、state が更新されたときに実行される関数で、サードパーティのライブラリを呼び出したり、<canvas> 要素を描画したり、ネットワークリクエストを行ったりするために使用できます。effect はブラウザ上でのみ実行され、サーバーサイドレンダリング中には実行されません。

一般的に、effect 内で state を更新するべきではありません。なぜなら、コードが複雑になり、無限ループに陥ることがあるからです。もしそのようなことをしているなら、$effect を使うべきでないとき のセクションを参照して、別のアプローチについて学んでください。

$effect rune を使って effect を作成します (デモ):

<script>
	let size = $state(50);
	let color = $state('#ff3e00');

	let canvas;

	$effect(() => {
		const context = canvas.getContext('2d');
		context.clearRect(0, 0, canvas.width, canvas.height);

		// これは `color` や `size` が変更されるたびに再実行されます
		context.fillStyle = color;
		context.fillRect(0, 0, size, size);
	});
</script>

<canvas bind:this={canvas} width="100" height="100"></canvas>

Svelte が effect 関数を実行するとき、どの state (および derived state) がアクセスされたかを追跡し (untrack の内側でアクセスされた場合を除く)、その state が後で変更されたときにその effect 関数を再実行します。

なぜ $effect が再実行されるのか、または再実行されないのかを理解するのが難しい場合は、依存関係を理解する を参照してください。Svelte 4 の $: ブロックとは異なる方法で effect がトリガーされます。

ライフサイクルを理解する

effect は、コンポーネントが DOM にマウントされた後に実行されます。また、state が変更された後には マイクロタスク で実行されます。再実行はバッチ処理され (つまり、同じ瞬間に colorsize を変更しても、2つの別々の実行は発生しません)、DOM 更新が適用された後に実行されます。

$effect は、親の effect の実行中に呼び出される限りは、コンポーネントのトップレベルだけでなく、どこでも使用できます。

Svelte はテンプレート内のロジックや式を表現するために内部的に effect を使用しています — これによって name の変更時に <h1>hello {name}!</h1> を更新しているのです。

effect は、effect の再実行の直前に実行される teardown 関数 を返すことができます (デモ)。

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

	$effect(() => {
		// これは `milliseconds` が変更されるたびに再作成されます
		const interval = setInterval(() => {
			count += 1;
		}, milliseconds);

		return () => {
			// teardown 関数が提供される場合、以下のときに実行されます
			// a) effect が再実行される直前
			// b) コンポーネントが破棄される前
			clearInterval(interval);
		};
	});
</script>

<h1>{count}</h1>

<button onclick={() => (milliseconds *= 2)}>slower</button>
<button onclick={() => (milliseconds /= 2)}>faster</button>

teardown 関数は、effect が破棄されるときにも実行されます。これは、親が破棄されたとき (例えば、コンポーネントがアンマウントされたとき) や親 effect が再実行されたときに発生します。

依存関係を理解する

$effect は、関数本体内で 同期的に 読み取られるリアクティブな値 ($state, $derived, $props) を (間接的なもの、関数呼び出し経由も含めて) 自動的に検出し、依存関係(dependencies)として登録します。これらの依存関係が変更されると、$effect は再実行をスケジュールします。

もし $state$derived$effect 内で直接使用されている場合 (例えば、リアクティブなclassの作成中など)、それらの値は依存関係として扱われません。

非同期的に読み取られる値 (例えば、await の後や setTimeout 内) は追跡されません。この例では、color が変更されると canvas が再描画されますが、size が変更されても再描画されません (デモ)。

function $effect(fn: () => void | (() => void)): void
namespace $effect

Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values. The timing of the execution is after the DOM has been updated.

Example:

$effect(() => console.log('The count is now ' + count));

If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.

Does not run during server side rendering.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
const const context: CanvasRenderingContext2Dcontext =
let canvas: {
    width: number;
    height: number;
    getContext(type: "2d", options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D;
}
canvas
.function getContext(type: "2d", options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2DgetContext('2d');
const context: CanvasRenderingContext2Dcontext.CanvasRect.clearRect(x: number, y: number, w: number, h: number): voidclearRect(0, 0,
let canvas: {
    width: number;
    height: number;
    getContext(type: "2d", options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D;
}
canvas
.width: numberwidth,
let canvas: {
    width: number;
    height: number;
    getContext(type: "2d", options?: CanvasRenderingContext2DSettings): CanvasRenderingContext2D;
}
canvas
.height: numberheight);
// これは `color` が変更されるたびに再実行されます... const context: CanvasRenderingContext2Dcontext.CanvasFillStrokeStyles.fillStyle: string | CanvasGradient | CanvasPatternfillStyle = let color: stringcolor; function setTimeout<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+2 overloads)

Schedules execution of a one-time callback after delay milliseconds.

The callback will likely not be invoked in precisely delay milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. The callback will be called as close as possible to the time specified.

When delay is larger than 2147483647 or less than 1 or NaN, the delay will be set to 1. Non-integer delays are truncated to an integer.

If callback is not a function, a TypeError will be thrown.

This method has a custom variant for promises that is available using timersPromises.setTimeout().

@sincev0.0.1
@paramcallback The function to call when the timer elapses.
@paramdelay The number of milliseconds to wait before calling the callback. Default: 1.
@paramargs Optional arguments to pass when the callback is called.
@returnsfor use with clearTimeout()
setTimeout
(() => {
// ...しかしこれは `size` が変更されても再実行されません const context: CanvasRenderingContext2Dcontext.CanvasRect.fillRect(x: number, y: number, w: number, h: number): voidfillRect(0, 0, let size: numbersize, let size: numbersize); }, 0); });

effect は、読み取るオブジェクトそのものが変更された場合にのみ再実行され、その中のプロパティが変更された場合には再実行されません。(オブジェクト内部の変更を開発時に観察したい場合は、$inspect を使用できます。)

<script>
	let state = $state({ value: 0 });
	let derived = $derived({ value: state.value * 2 });

	// これは一度だけ実行されます。なぜなら `state` は再代入されていないからです (変異(mutated)のみです)
	$effect(() => {
		state;
	});

	// これは `state.value` が変更されるたびに実行されます...
	$effect(() => {
		state.value;
	});

	// ...これも `state.value` が変更されるたびに実行されます。なぜなら `derived` は毎回新しいオブジェクトになるからです
	$effect(() => {
		derived;
	});
</script>

<button onclick={() => (state.value += 1)}>
	{state.value}
</button>

<p>{state.value} doubled is {derived.value}</p>

effect は、前回実行されたときに読み取られた値のみに依存します。これは、条件付きコードを持つ effect にとって興味深い含意があります。

例えば、以下のコードスニペットで conditiontrue の場合、if ブロック内のコードが実行され、color が評価されます。そのため、condition または color の変更が effect を再実行を引き起こします

一方で、conditionfalse の場合、color は評価されず、 effect は condition が変更されたときに のみ 再実行されます。

import function confetti(opts?: ConfettiOptions): voidconfetti from 'canvas-confetti';

let let condition: booleancondition = 
function $state<true>(initial: true): true (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
(true);
let let color: stringcolor =
function $state<"#ff3e00">(initial: "#ff3e00"): "#ff3e00" (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dev/docs/svelte/$state

@paraminitial The initial value
$state
('#ff3e00');
function $effect(fn: () => void | (() => void)): void
namespace $effect

Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values. The timing of the execution is after the DOM has been updated.

Example:

$effect(() => console.log('The count is now ' + count));

If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.

Does not run during server side rendering.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
if (let condition: truecondition) { function confetti(opts?: ConfettiOptions): voidconfetti({ ConfettiOptions.colors: string[]colors: [let color: stringcolor] }); } else { function confetti(opts?: ConfettiOptions): voidconfetti(); } });

$effect.pre

稀なケースでは、DOM 更新の 前に コードを実行する必要がある場合があります。そのためには $effect.pre rune を使用できます:

<script>
	import { tick } from 'svelte';

	let div = $state();
	let messages = $state([]);

	// ...

	$effect.pre(() => {
		if (!div) return; // まだマウントされていない場合

		// `messages` 配列の length が変わるたびにこのコードを再実行するため、ここで参照する
		messages.length;

		// 新しい message が追加されるたびに自動でスクロールする
		if (div.offsetHeight + div.scrollTop > div.scrollHeight - 20) {
			tick().then(() => {
				div.scrollTo(0, div.scrollHeight);
			});
		}
	});
</script>

<div bind:this={div}>
	{#each messages as message}
		<p>{message}</p>
	{/each}
</div>

タイミングを除けば、$effect.pre$effect とまったく同じように機能します。

$effect.tracking

$effect.tracking rune は高度な機能で、コードが effect やテンプレートなどの tracking context の中でで実行されているかどうかを示します (デモ):

<script>
	console.log('in component setup:', $effect.tracking()); // false

	$effect(() => {
		console.log('in effect:', $effect.tracking()); // true
	});
</script>

<p>in template: {$effect.tracking()}</p> <!-- true -->

これは、createSubscriber のような抽象化を実装するために使用されます。これにより、リアクティブな値を更新するリスナーが作成されますが、それらの値が追跡されている場合に のみ 実行されます (例えば、イベントハンドラー内で読み取られる場合は除きます)。

$effect.pending

コンポーネントで await を使用する場合、$effect.pending() rune は現在の boundary で pending 状態の promise の数を示します。子の booundary は含まれません (デモ):

<button onclick={() => a++}>a++</button>
<button onclick={() => b++}>b++</button>

<p>{a} + {b} = {await add(a, b)}</p>

{#if $effect.pending()}
	<p>pending promises: {$effect.pending()}</p>
{/if}

$effect.root

$effect.root rune は高度な機能で、自動クリーンアップされない非トラッキングスコープ (non-tracked scope) を作成します。これは、手動で制御したいネストされた effect に便利です。また、この rune は、コンポーネント初期化フェーズの外部で effect を作成することも可能にします。

const const destroy: () => voiddestroy = 
namespace $effect
function $effect(fn: () => void | (() => void)): void

Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values. The timing of the execution is after the DOM has been updated.

Example:

$effect(() => console.log('The count is now ' + count));

If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.

Does not run during server side rendering.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
.function $effect.root(fn: () => void | (() => void)): () => void

The $effect.root rune is an advanced feature that creates a non-tracked scope that doesn’t auto-cleanup. This is useful for nested effects that you want to manually control. This rune also allows for creation of effects outside of the component initialisation phase.

Example:

&#x3C;script>
  let count = $state(0);

  const cleanup = $effect.root(() => {
	$effect(() => {
			console.log(count);
		})

	 return () => {
	   console.log('effect root cleanup');
			}
  });
&#x3C;/script>

&#x3C;button onclick={() => cleanup()}>cleanup&#x3C;/button>

https://svelte.dev/docs/svelte/$effect#$effect.root

root
(() => {
function $effect(fn: () => void | (() => void)): void
namespace $effect

Runs code when a component is mounted to the DOM, and then whenever its dependencies change, i.e. $state or $derived values. The timing of the execution is after the DOM has been updated.

Example:

$effect(() => console.log('The count is now ' + count));

If you return a function from the effect, it will be called right before the effect is run again, or when the component is unmounted.

Does not run during server side rendering.

https://svelte.dev/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
// setup }); return () => { // cleanup }; }); // later... const destroy: () => voiddestroy();

$effect を使うべきでないとき

一般的に、$effect は脱出口 (エスケープハッチ) のようなものと考えるのが最善です。たとえば、アナリティクス や直接的な DOM 操作などに便利ですが、頻繁に使用するツールではありません。特に、state を同期するために使用するのは避けてください。このように使用するのではなく...

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

	// don't do this!
	$effect(() => {
		doubled = count * 2;
	});
</script>

...このようにしてください:

<script>
	let count = $state(0);
	let doubled = $derived(count * 2);
</script>

count * 2 のような単純な式よりも複雑なものの場合は、$derived.by を使用することもできます。

(例えば楽観的 UI を作るために) derived な値に再代入するために effect を使用しているなら、Svelte 5.25 からは derived を直接オーバーライドできる ようになったことにご留意ください。

effect を使ってある値を別の値に関連付けるような複雑な処理をしたくなるかもしれません。次の例は、「支出金額(money spent)」と「残高(money left)」という2つの入力が互いに連動していることを示しています。一方を更新すると、もう一方もそれに応じて更新されるべきです。このようなケースで effect を使用しないでください (デモ):

<script>
	const total = 100;
	let spent = $state(0);
	let left = $state(total);

	$effect(() => {
		left = total - spent;
	});

	$effect(() => {
		spent = total - left;
	});
</script>

<label>
	<input type="range" bind:value={spent} max={total} />
	{spent}/{total} spent
</label>

<label>
	<input type="range" bind:value={left} max={total} />
	{left}/{total} left
</label>

代わりに、可能な場合は oninput コールバック か — better still — function bindings を使用してください (デモ):

<script>
	const total = 100;
	let spent = $state(0);
	let left = $derived(total - spent);

	function updateLeft(left) {
		spent = total - left;
	}
</script>

<label>
	<input type="range" bind:value={spent} max={total} />
	{spent}/{total} spent
</label>

<label>
	<input type="range" bind:value={() => left, updateLeft} max={total} />
	{left}/{total} left
</label>

どうしても effect 内で $state を更新する必要があり、同じ $state を読み書きすることで無限ループに陥った場合は、untrack を使用してください。

Edit this page on GitHub llms.txt

previous next