0
0

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 3 years have passed since last update.

readonly な state を保持する store に reset method を実装する(Pinia)

0
Posted at

環境

  • Vue.js - 3.2.13
  • pinia - 2.0.28
  • lodash.clonedeep - 4.5.0

結論

  • readonly store の実態は Proxy(MDN)
  • ProxyresetにはObject.assign()を使え(stackoverflow
  • Proxytargetへの参照を保持しているに過ぎない
    1. store内部でno readonlyraw stateを定義する
    2. raw stateresetする。
    3. 外部からの参照を許可するstatereadonlyにする

code 抜粋

stores/store.js
// ...
import cloneDeep from "lodash.clonedeep";
  // ...
  const target = {
    name: "taro",
    age: 18,
  };

  // (1.) raw state
  const state = reactive({ ...cloneDeep(target) });

  const actions = {
    reset() {
      // (2.) raw state をリセット
      Object.assign(state, { ...cloneDeep(target) });
      return this;
    },
  // ...
  return {
    // (3.) raw state を readonly にして公開
    ...toRefs(readonly(state)),
    ...actions,
  };
}

前提にある問題

  • $reset()公式Doc)は用意されているが、Option Stores公式Doc)の書式では$reset()が定義される。
    • 但し、readonlyには出来ない。(※最低でも私は実装方法を知らない)
  • 逆にSetup Store公式Doc)の書式の場合は、$reset は定義されない為、呼び出し時にエラーになる。
    • 但し、readonlyに出来る(※重要)
Option Store の例
export const useOptionStore = defineStore('option-store', {

  state: () => ({ name: 'taro' }),

  actions: {
    setName(name) {
      this.state.name = name;
    },
  },
});
Setup Store の例
export const useSetupStore = defineStore('setup-store', () => {

  const state = {
    name: ref('taro'),
  };
  
  const actions = {
    setName(name) {
      state.name = name;
    },
  };

  return {
    ...state,
    ...actions,
  }
});
readonly な store の例
export const useReadonlyStore = defineStore('readonly-store', () => {

  const state = {
    name: ref('taro'),
  };
  
  const actions = {
    setName(name) {
      state.name = name;
    },
  };

  return {
    ...toRefs(readonly(state)),
    ...actions,
  };
});
  • readonly な state を保持する store に例として紹介されている$reset methodでは正しく処理できない
  • 外部呼び出し可能な $reset method を override しているので、外部参照時に readonly なプロパティの変更(pinia.$patch())で warning になる。
よくある $reset() メソッドの override 案
// [Vue.js] main.js
import { createPinia } from 'pinia';
import cloneDeep from 'lodash.clonedeep';

const pinia = createPinia();

pinia.use(({ store }) => {
  const initialState = cloneDeep(store.$state)
  pinia.$reset = () => pinia.$patch(cloneDeep(initialState))
})
正しく動作する例
<template>
  <button @click="reset">reset</button>
</template>

<script>
import { useSetupStore } from "./stores/setup-store.js"; // セットアップストア

export default {
  setup(){
    const setupStore = useSetupStore();

    return {
        setupStore,
    };
  },

  methods:{
    reset(){
      this.setupStore.$reset(); // Success!!
    }
  }
}
</sctipr>
動作しない例
<template>
  <button @click="reset">reset</button>
</template>

<script>
import { useReadonlyStore } from "./stores/readonly-store.js"; // readonly なセットアップストア

export default {
  setup(){
    const readonlyStore = useReadonlyStore();

    return {
        readonlyStore,
    };
  },

  methods:{
    reset(){
      this.readonlyStore.$reset(); // Warning!!
    }
  }
}
</sctipr>

参考

readonly store の reset code 全体

stores/store.js
import { reactive, readonly, toRefs } from "vue";
import { defineStore } from "pinia";
import cloneDeep from "lodash.clonedeep";

export const useSampleStore = defineStore("sample-store", () => {
  // target state
  const target = {
    name: "taro",
    age: 18,
  };

  // raw state
  const state = reactive({ ...cloneDeep(target) });

  const actions = {
    // reset method
    reset() {
      Object.assign(state, { ...cloneDeep(target) });
      return this;
    },

    // setter
    setName(name) {
      state.name = name;
      return this;
    },

    // setter
    setAge(age) {
      state.age = age;
      return this;
    },
  };

  return {
    // readonly
    ...toRefs(readonly(state)),
    ...actions,
  };
});

リンク

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?