vuejs / pinia

🍍 Intuitive, type safe, light and flexible Store for Vue using the composition api with DevTools support
https://pinia.vuejs.org
MIT License
13.1k stars 1.05k forks source link

store defined with function and ref as state variable not working correctly #1047

Closed m0dch3n closed 2 years ago

m0dch3n commented 2 years ago

Reproduction

Please visit Stackblitz to check the 4 different (d, fp, fap, fnp) simple stores I created.

Actual behavior

Check the console: fp and fnp are not console logged during the subscription

  store.$subscribe((mutation) => {
    console.log('mutation', mutation.events.newValue);
  });

Check the localstorage: fp is not persisted

  if (options.persist) {
    for (const persistKey of options.persist) {
      console.log(persistKey);
      store.$state[persistKey] = useLocalStorage(
        store.$id,
        store.$state[persistKey]
      );
    }
  }

and

  { persist: ['myStateVar'] }

The difference between fp and fap is:

fp contains Ref fap contains RemovableRef from @vueuse/core

Expected behavior

fp and fnp should callback the subscription (seems to be related when the var is a ref) d, fap, fp should all be persisted (when overwriting the ref in the plugin with useLocalStorage seems not to fix the issue of fp)

posva commented 2 years ago

You need to write to store too:

if (options.persist) {
    for (const persistKey of options.persist) {
      console.log(persistKey);
      const v = useLocalStorage(
        store.$id,
        store.$state[persistKey]
      );
      store[persistKey] = v
      store.$state[persistKey] = v
    }
  }
m0dch3n commented 2 years ago

@posva

Thank you for your quick reply and help! I fixed it and applied it on my stackblitz test environment.

However a new issue now raised:

import { defineStore, acceptHMRUpdate } from 'pinia';
import { ref, computed } from 'vue';

export const useFPStore = defineStore(
  'fp',
  () => {
    const myStateVar = ref('init fp');

    const myComputed = computed(() => {
      const fp = useFPStore();
      console.log('myStateVar.value from fp', myStateVar.value);
      console.log('fp.myStateVar', fp.myStateVar);
      return myStateVar.value;
    });
    return {
      myStateVar,
      myComputed,
    };
  },
  { persist: ['myStateVar'] }
);

if (import.meta.hot) {
  import.meta.hot.accept(acceptHMRUpdate(useFPStore, import.meta.hot));
}

By assigning v to store[persistKey] I lose unfortunately the reference to

myStateVar.value in my computed getter, due to the override in plugin.

(the value stays to 'init fp' for myStateVar.value)

In your documentation you pass (state) to each getter. Is there a similar API maybe I can use by defining my store with a function ?

Or is the best way, to inject the current state in the computed by myself with const fp = useFPStore();

m0dch3n commented 2 years ago

PS:

I know I can also have the useLocalStorage since the beginning, but this would make my code less flexible...

I prefer doing it by plugin.

And of course another idea, which I could do for my persistPlugin, is to subscribe to the mutations and do the getItem, setItem etc myself, but this is more boiler code, and looks less nicer...