Skip to main content

$effect

Effects are functions that run when state updates, and can be used for things like calling third-party libraries, drawing on <canvas> elements, or making network requests. They only run in the browser, not during server-side rendering.

Generally speaking, you should not update state inside effects, as it will make code more convoluted and will often lead to never-ending update cycles. If you find yourself doing so, see when not to use $effect to learn about alternative approaches.

You can create an effect with the $effect rune (demo):

<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>

When Svelte runs an effect function, it tracks which pieces of state (and derived state) are accessed (unless accessed inside untrack), and re-runs the function when that state later changes.

If you’re having difficulty understanding why your $effect is rerunning or is not running see understanding dependencies. Effects are triggered differently than the $: blocks you may be used to if coming from Svelte 4.

Understanding lifecycle

Your effects run after the component has been mounted to the DOM, and in a microtask after state changes. Re-runs are batched (i.e. changing color and size in the same moment won’t cause two separate runs), and happen after any DOM updates have been applied.

You can use $effect anywhere, not just at the top level of a component, as long as it is called while a parent effect is running.

Svelte uses effects internally to represent logic and expressions in your template — this is how <h1>hello {name}!</h1> updates when name changes.

An effect can return a teardown function which will run immediately before the effect re-runs (demo).

<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 functions also run when the effect is destroyed, which happens when its parent is destroyed (for example, a component is unmounted) or the parent effect re-runs.

依存関係を理解する

$effect automatically picks up any reactive values ($state, $derived, $props) that are synchronously read inside its function body (including indirectly, via function calls) and registers them as dependencies. When those dependencies change, the $effect schedules a re-run.

If $state and $derived are used directly inside the $effect (for example, during creation of a reactive class), those values will not be treated as dependencies.

非同期的に読み取られる値 (例えば、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, ms?: 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, 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.
@paramargs Optional arguments to pass when the callback is called.
@returnfor use with {@link 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.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>
	let 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>
	let total = 100;
	let spent = $state(0);
	let left = $state(total);

	function updateSpent(value) {
		spent = value;
		left = total - spent;
	}

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

<label>
	<input type="range" bind:value={() => spent, updateSpent} 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

previous next