メインコンテンツへスキップ

$effect

エフェクトは、アプリケーションに動作をさせるものです。Svelteがエフェクト関数を実行すると、どの状態(および派生状態)がアクセスされているかを追跡し(untrackの内部でアクセスされている場合を除く)、その状態が後で変更されたときに、関数を再実行します。

Svelteアプリのエフェクトのほとんどは、Svelte自身によって作成されています。たとえば、nameが変更されたときに<h1>hello {name}!</h1>のテキストを更新するビットです。

ただし、$effect runeを使用して独自のエフェクトを作成することもできます。これは、外部システム(ライブラリ、<canvas>要素、またはネットワークを介したものなど)をSvelteアプリ内の状態と同期する必要がある場合に便利です。

$effectの過剰使用は避けてください! エフェクトで多くの作業を行うと、コードが理解および保守するのが難しくなることがよくあります。代替のアプローチについては、$effectを使用しない場合を参照してください。

エフェクトは、コンポーネントがDOMにマウントされた後、状態が変更された後のマイクロタスクで実行されます(デモ)。

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

		// this will re-run whenever `color` or `size` change
		context.fillStyle = color;
		context.fillRect(0, 0, size, size);
	});
</script>

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

再実行はバッチ処理され(つまり、同じ瞬間にcolorsizeを変更しても、2つの別々の実行は発生しません)、DOMの更新が適用された後に発生します。

コンポーネントの初期化中(または親エフェクトがアクティブな間)に呼び出される限り、$effectはコンポーネントのトップレベルだけでなく、どこにでも配置できます。その後、コンポーネント(または親エフェクト)のライフサイクルに紐付けられ、コンポーネントがアンマウントされる(または親エフェクトが破棄される)と自動的に破棄されます。

$effectから関数を返すことができます。これは、エフェクトが再実行される直前、および破棄される前に実行されます(デモ)。

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

	$effect(() => {
		// This will be recreated whenever `milliseconds` changes
		const interval = setInterval(() => {
			count += 1;
		}, milliseconds);

		return () => {
			// if a callback is provided, it will run
			// a) immediately before the effect re-runs
			// b) when the component is destroyed
			clearInterval(interval);
		};
	});
</script>

<h1>{count}</h1>

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

依存関係の理解

$effectは、関数本体内で同期的に読み取られたリアクティブ値($state$derived$props)を自動的に取得し、依存関係として登録します。これらの依存関係が変更されると、$effectは再実行をスケジュールします。

たとえば、awaitの後やsetTimeoutの内部など、非同期的に読み取られる値は追跡されません。ここでは、キャンバスはcolorが変更されると再描画されますが、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.dokyumento.jp/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);
// this will re-run whenever `color` changes... 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
(() => {
// ...but not when `size` changes const context: CanvasRenderingContext2Dcontext.CanvasRect.fillRect(x: number, y: number, w: number, h: number): voidfillRect(0, 0, let size: numbersize, let size: numbersize); }, 0); });

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

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

	// this will run once, because `state` is never reassigned (only mutated)
	$effect(() => {
		state;
	});

	// this will run whenever `state.value` changes...
	$effect(() => {
		state.value;
	});

	// ...and so will this, because `derived` is a new object each time
	$effect(() => {
		derived;
	});
</script>

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

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

エフェクトは、最後に実行されたときに読み取った値にのみ依存します。aがtrueの場合、bへの変更はこのエフェクトを再実行させることはありません

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.dokyumento.jp/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without calling require('console').

Warning: The global console object’s methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
//   Error: Whoops, something bad happened
//     at [eval]:5:15
//     at Script.runInThisContext (node:vm:132:18)
//     at Object.runInThisContext (node:vm:309:38)
//     at node:internal/process/execution:77:19
//     at [eval]-wrapper:6:22
//     at evalScript (node:internal/process/execution:76:60)
//     at node:internal/main/eval_string:23:3

const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);

myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
@seesource
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100
log
('running');
if (let a: falsea || let b: falseb) { var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
  • A global console instance configured to write to process.stdout and process.stderr. The global console can be used without calling require('console').

Warning: The global console object’s methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:

console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
//   Error: Whoops, something bad happened
//     at [eval]:5:15
//     at Script.runInThisContext (node:vm:132:18)
//     at Object.runInThisContext (node:vm:309:38)
//     at node:internal/process/execution:77:19
//     at [eval]-wrapper:6:22
//     at evalScript (node:internal/process/execution:76:60)
//     at node:internal/main/eval_string:23:3

const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr

Example using the Console class:

const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);

myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
@seesource
console
.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stdout with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout

See util.format() for more information.

@sincev0.1.100
log
('inside if block');
} });

$effect.pre

まれに、DOMの更新にコードを実行する必要がある場合があります。このために、$effect.pre runeを使用できます。

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

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

	// ...

	$effect.pre(() => {
		if (!div) return; // not yet mounted

		// reference `messages` array length so that this code re-runs whenever it changes
		messages.length;

		// autoscroll when new messages are added
		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は、エフェクト内やテンプレート内など、コードが追跡コンテキスト内で実行されているかどうかを示す高度な機能です(デモ

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

これにより、たとえば、子エフェクトに入れることで、メモリリークを引き起こすことなく、サブスクリプションのようなものを追加できます。これは、追跡コンテキスト内にある限り、コールバック関数からの変更をリッスンするreadable関数です。

import { function tick(): Promise<void>

Returns a promise that resolves once any pending state changes have been applied.

tick
} from 'svelte';
export default function
function readable<T>(initial_value: T, start: (callback: (update: (v: T) => T) => T) => () => void): {
    readonly value: T;
}
readable
<
function (type parameter) T in readable<T>(initial_value: T, start: (callback: (update: (v: T) => T) => T) => () => void): {
    readonly value: T;
}
T
>(
initial_value: Tinitial_value:
function (type parameter) T in readable<T>(initial_value: T, start: (callback: (update: (v: T) => T) => T) => () => void): {
    readonly value: T;
}
T
,
start: (callback: (update: (v: T) => T) => T) => () => voidstart: (callback: (update: (v: T) => T) => Tcallback: (update: (v: T) => Tupdate: (v: Tv:
function (type parameter) T in readable<T>(initial_value: T, start: (callback: (update: (v: T) => T) => T) => () => void): {
    readonly value: T;
}
T
) =>
function (type parameter) T in readable<T>(initial_value: T, start: (callback: (update: (v: T) => T) => T) => () => void): {
    readonly value: T;
}
T
) =>
function (type parameter) T in readable<T>(initial_value: T, start: (callback: (update: (v: T) => T) => T) => () => void): {
    readonly value: T;
}
T
) => () => void
) { let let value: Tvalue =
function $state<T>(initial: T): T (+1 overload)
namespace $state

Declares reactive state.

Example:

let count = $state(0);

https://svelte.dokyumento.jp/docs/svelte/$state

@paraminitial The initial value
$state
(initial_value: Tinitial_value);
let let subscribers: numbersubscribers = 0; let let stop: (() => void) | nullstop: null | (() => void) = null; return { get value: Tvalue() { // If in a tracking context ... if (
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.dokyumento.jp/docs/svelte/$effect

@paramfn The function to execute
$effect
.function $effect.tracking(): boolean

The $effect.tracking rune is an advanced feature that tells you whether or not the code is running inside a tracking context, such as an effect or inside your template.

Example:

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

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

&#x3C;p>in template: {$effect.tracking()}&#x3C;/p> &#x3C;!-- true -->

This allows you to (for example) add things like subscriptions without causing memory leaks, by putting them in child effects.

https://svelte.dokyumento.jp/docs/svelte/$effect#$effect.tracking

tracking
()) {
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.dokyumento.jp/docs/svelte/$effect

@paramfn The function to execute
$effect
(() => {
// ...and there's no subscribers yet... if (let subscribers: numbersubscribers === 0) { // ...invoke the function and listen to changes to update state let stop: (() => void) | nullstop = start: (callback: (update: (v: T) => T) => T) => () => voidstart((fn: (v: T) => Tfn) => (let value: Tvalue = fn: (v: T) => Tfn(let value: Tvalue))); } let subscribers: numbersubscribers++; // The return callback is called once a listener unlistens return () => { function tick(): Promise<void>

Returns a promise that resolves once any pending state changes have been applied.

tick
().Promise<void>.then<void, never>(onfulfilled?: ((value: void) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>

Attaches callbacks for the resolution and/or rejection of the Promise.

@paramonfulfilled The callback to execute when the Promise is resolved.
@paramonrejected The callback to execute when the Promise is rejected.
@returnsA Promise for the completion of which ever callback is executed.
then
(() => {
let subscribers: numbersubscribers--; // If it was the last subscriber... if (let subscribers: numbersubscribers === 0) { // ...stop listening to changes let stop: (() => void) | nullstop?.(); let stop: (() => void) | nullstop = null; } }); }; }); } return let value: Tvalue; } }; }

$effect.root

$effect.root runeは、自動クリーンアップされない、追跡されないスコープを作成する高度な機能です。これは、手動で制御したいネストされたエフェクトに役立ちます。このruneは、コンポーネントの初期化フェーズ外でエフェクトを作成することもできます。

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

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

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

$effectを使用しない場合

一般に、$effectは、頻繁に使用するツールというよりも、分析や直接DOM操作のようなものに役立つ、脱出ハッチのようなものと考えるのが最適です。特に、状態を同期するために使用することは避けてください。これの代わりに...

<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を使用することもできます。

ある値を別の値にリンクするために、エフェクトを使用して複雑なことをしたくなるかもしれません。次の例は、「お金を使った」と「お金が残った」の2つの入力が互いに接続されていることを示しています。1つを更新すると、もう1つがそれに応じて更新される必要があります。このためにエフェクトを使用しないでください(デモ

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

代わりに、可能な場合はコールバックを使用してください(デモ

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

	function updateSpent(e) {
		spent = +e.target.value;
		left = total - spent;
	}

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

<label>
	<input type="range" value={spent} oninput={updateSpent} max={total} />
	{spent}/{total} spent
</label>

<label>
	<input type="range" value={left} oninput={updateLeft} max={total} />
	{left}/{total} left
</label>

何らかの理由でバインディングを使用する必要がある場合(たとえば、「書き込み可能な$derived」が必要な場合)、ゲッターとセッターを使用して状態を同期することを検討してください(デモ

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

	let left = {
		get value() {
			return total - spent;
		},
		set value(v) {
			spent = total - v;
		}
	};
</script>

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

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

エフェクト内で$stateを更新する必要があり、同じ$stateを読み書きするために無限ループに陥った場合は、untrackを使用してください。

このページをGitHubで編集

前へ 次へ