vize:croquis/cf/watcheffect-async
watchEffect が async タスクを始め、前回の実行を片付けられません。
既定の重大度: error
適用範囲: 解析対象のコンポーネント構成と、下記に示す対応済みの情報
自動修正: なし。関連ファイルを確認して修正してください
オプション: コード固有のオプションはありません。対応済みの CLI 検出は重大度を変更できます
import { defineConfig } from "@vizejs/vite-plugin/vite-plus";
export default defineConfig({
lint: {
vize: { preset: "incremental", crossFile: true,
rules: { "croquis/cf/watcheffect-async": "warn" },
},
},
});
vp run lint
共通のプロジェクト ファイル
以下のファイルは悪い例・良い例で共通です。import する Vue と、例で使う場合は vue-router / Pinia をインストールしてください。バージョン固有の注意がある場合は、その前提に合わせてください。エントリー ファイルでコンポーネントの関係を明確にしています。
main.ts
import { createApp } from "vue";
import Root from "./SearchPage.vue";
createApp(Root).mount("#app");
index.html
<div id="app"></div>
<script type="module" src="/main.ts"></script>
api.ts
export interface Result { items: string[]; }
export async function load(query: string, options?: { signal?: AbortSignal }): Promise<Result> {
const response = await fetch(`/search?q=${encodeURIComponent(query)}`, options);
return response.json();
}
悪い
async watchEffect に暗黙の依存収集と await する request が混在し、無効化後の応答を防ぐ処理がありません。
SearchPage.vue
<script setup lang="ts">
import { ref } from "vue";
import SearchResults from "./SearchResults.vue";
const query = ref("");
</script>
<template>
<SearchResults :query="query" />
</template>
SearchResults.vue
<script setup lang="ts">
import { load, type Result } from "./api";
import { ref, watchEffect } from "vue";
const props = defineProps<{ query: string }>();
const result = ref<Result | null>(null);
watchEffect(async () => {
result.value = await load(props.query);
});
</script>
良い
watch(() => props.query, ...) で source を明示し、request の cleanup と無効化後の応答を除く条件を付けます。
SearchPage.vue
<script setup lang="ts">
import { ref } from "vue";
import SearchResults from "./SearchResults.vue";
const query = ref("");
</script>
<template>
<SearchResults :query="query" />
</template>
SearchResults.vue
<script setup lang="ts">
import { load, type Result } from "./api";
import { ref, watch } from "vue";
const props = defineProps<{ query: string }>();
const result = ref<Result | null>(null);
watch(
() => props.query,
async (value, _oldValue, onCleanup) => {
const controller = new AbortController();
let active = true;
onCleanup(() => {
active = false;
controller.abort();
});
const next = await load(value, { signal: controller.signal });
if (active) result.value = next;
},
);
</script>
良い例のファイルは上で説明した変更を示します。プロジェクトには別の検出が残る場合があります。