0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vue3の reactive なリストの中でcomputedを使ってみる

Last updated at Posted at 2023-03-13

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に

image.png

bを35に

image.png

sumが55に変わった!
しかも画面描画の度に再計算されずちゃんとキャッシュを保持してくれてます。

他の実装方法

以下の実装は画面描画の都度再計算されるのでコスパ悪い

  • v-forのループ内で関数を呼び出す
  • Vue2系からGoogle検索で見かけたcomputedに引数
    //例
    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オブジェクト
    });
    
    上記computedでsumを計算した場合
    image.png
    一つの値を操作すると操作していない方も再計算される。
    image.png

一般的には値を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にデモページも・・・

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?