コンテンツにスキップ
svelte-vitals
日本語
Esc
移動開く⌘Jプレビュー
このページの内容

correctness/effect-as-onmount · onMount 代わりの $effect

reactive 値を読まない $effect は、イベントハンドラ、{@attach}、onMount のいずれかで書くべきです。

重大度: warning · カテゴリ: correctness

チェック内容

コンポーネントのインスタンススクリプトで、本体が空ではないのに、このチェックから見える reactive 値を一切読まない $effect / $effect.pre を検出します。$state$derived$props、import されたバインディング、new …() を初期化子として宣言されたローカル変数、store 購読のいずれもなく、裸の関数呼び出し(foo())もないものです。そのような effect はマウント後に一度だけ実行され、このチェックが追跡できる範囲では再実行されません。

なぜ重要か

何にも反応しない $effect は、多くの場合イベントハンドラ、{@attach}onMount の代わりとして遠回りなだけです。$effect で書くとその意図が読み取りにくくなるうえ、リアクティビティの仕組みの誤用でもあります。

修正方法

<script>
  import { onMount } from 'svelte';
  // $effect(() => { element.focus(); }); の代わりに
  onMount(() => {
    element.focus();
  });
</script>

ユーザー操作への応答として実行しているなら、$effect よりイベントハンドラを優先してください。外部ライブラリ(チャートやツールチップなど)と要素を同期しているなら、{@attach ...}(5.29 以降)を優先してください。$effectonMount より現在推奨されている DOM/ライブラリ同期の方法です。

既知の制限

このチェックは、rune の宣言($state/$derived/$props)、import されたバインディング、new …() を初期化子として宣言されたローカル変数(const x = new Foo())など、たどれる名前を経由した reactive 読み取りを認識します。これはクラスの $state フィールド、SvelteMap/SvelteSet、import された runes モジュールの状態オブジェクト、svelte/reactivity/window をカバーします。以下の2つの形はたどれる名前がないため、そのように書かれた本物の reactive な effect でも検出されてしまうことがあります。

  • 素の関数の戻り値(const c = createCounter())経由でしか到達できない reactive な値。
  • 宣言時ではなく宣言後に new …() を代入したローカル変数(let m; m = new SvelteMap();)。初期化子として宣言された形のみ認識されます。

名前の照合は字句スコープではなく識別子のテキストで行われます。これは rune 名に対してこのルールが元々使っている粒度と同じです。そのため、import または new で宣言された名前をシャドーするコールバックローカルな binding(例えば同じ名前を再利用するパラメータ)も reactive として扱われます。これは指摘を見逃す方向にしか働かず、誤って検出することはありません。

該当する場合は、動作しているコードを onMount に移すのではなく、 svelte-vitals-disable-next-line コメントで抑制してください。

モードによる違い

ありません。このルールが読むのは同じ .svelte / .ts のソースファイルなので、CLI、Vite プラグインのビルド、ライブダッシュボードの静的ベースラインのいずれでも結果は同一で、レンダリング済み HTML の解析で再評価されることもありません。--route で実行範囲を絞ると、このルールは動きません。コンポーネントスコープのルールには、検出を紐づけるルートが無いためです。

無効化

個別に抑制するには、対象行の直前に <!-- svelte-vitals-disable-next-line correctness/effect-as-onmount --> を置きます。ルールごと無効化するには、次のように設定します。

export default {
  rules: {
    'correctness/effect-as-onmount': 'off'
  }
};