Vue3の reactive なリストの中でcomputedを使ってみる
reactiveの中にcomputed・・・
import dayjs from 'dayjs';
import { reactive, computed, ref } from 'vue';
const state = reactive({
listA: {},
})
const appendListA = () => {
const ts = dayjs().format('YYYY-MM-DD HH:mm:ss.SSS');
state.listA[ts] = {
data: { a: 0, b: 0 },
sum: ref(0),//初期値はrefを格納
};
state.listA[ts].sum = computed(() => {
console.log('listA', ts, 'computed', state.listA[ts].data);
return state.listA[ts].data.a + state.listA[ts].data.b;
});
};
stateの型定義的はこんな感じ
interface State {
listA: {
[key: string]: ListRowA;
};
}
interface ListRowA {
data: { a: number; b: number };
sum: ComputedRef<number> | Ref<number>;
}
あとはaとbを操作するテンプレートを用意すれば・・・
aを20に
bを35に
sumが55に変わった!
しかも画面描画の度に再計算されずちゃんとキャッシュを保持してくれてます。
他の実装方法
以下の実装は画面描画の都度再計算されるのでコスパ悪い
- v-forのループ内で関数を呼び出す
- Vue2系からGoogle検索で見かけたcomputedに引数
上記computedでsumを計算した場合
//例 interface ListRowB { data: { a: number; b: number }; } interface State { listB: { [key: string]: ListRowB; }; } const state = reactive<State>({ listB: {}, }); const sumListB = computed(() => (ts: string, row: ListRowB) => { console.log('listB', ts, 'computed', row); return row.data.a + row.data.b;//rowは一応Proxyオブジェクト });
一つの値を操作すると操作していない方も再計算される。
一般的には値をdeepにWatchして変更時にreactiveなオブジェクト内に計算結果を保管?でしょうか。
もしくは集計用配列をcomputedで作るとか?
//集計用・・・・これもcomputedに引数と一緒で再計算されますね・・・・・
const sumList = computed(() => {
return Object.keys(state.listB)
.map((key) => {
return {
key: key,
value: state.listB[key].data.a + state.listB[key].data.b,
};
})
.reduce((ret, row) => {
ret[row.key] = row.value;
return ret;
}, {} as { [key: string]: number });
});
最後に
ちなみにサンプルコードがキーバリューなリストにしているのはたまたまです。
ArrayだとRefの部分にcomputedを代入するときnumberで型エラーが・・・記事書いてて気づいた。
回避策未確認。
ProxyなreactiveオブジェクトってClass内に設置してClassの関数からthis経由で触ってもVueが変更検知できたり意味不明レベルでストーカーな監視体制なのビックリですね・・・・・
ほんとに雑に扱っても監視されるw
※Classの中でreactiveを使う例
<script setup lang="ts">
import { reactive } from 'vue';
class Hoge {
public state = reactive({ step: 0 });
public step() {
this.state.step += 1;
}
}
const hoge = new Hoge();
</script>
<template>
<div class="" @click="hoge.step()">hoge{{ hoge.state.step }}</div>
</template>
GitHubにサンプル上げてます
ついでにGitHubにデモページも・・・