Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pinia persisted state: storages (session storage) #356

Open
tamysiby opened this issue Jul 12, 2023 · 2 comments
Open

pinia persisted state: storages (session storage) #356

tamysiby opened this issue Jul 12, 2023 · 2 comments
Labels
question Further information is requested

Comments

@tamysiby
Copy link

Hi, thanks so much for this project! I manage to get most of my project running, but I can't seem to manage to use pinia-plugin-persistedstate and its sessionStorage without getting hacky. Is there any config part that I missed out on or is that something in progress?

I've added if (typeof window !== 'undefined') pinia.use(piniaPluginPersistedstate) in the store config, and added { persist: typeof window === 'undefined' ? false : { storage: sessionStorage, }, } in my individual stores to make sure they're running in the client side. Despite causing glitches, I guess it works. Just wondered if there are configs for pinia or rehydration. Thank you again!

@freddy38510
Copy link
Owner

freddy38510 commented Jul 13, 2023

I've played around with this plugin a few days ago.

Instead of using type of window !== 'undefined', you can use !import.meta.env.SSR or process.env.CLIENT. It will be statically replaced by true or false when the app is compiled, allowing tree-shaking of unreachable code.

When a pre-rendered page is fully loaded by the browser, the store is primed with the server-initialized state. But when navigating to subsequent pages (you are in SPA mode now), there is no server-initialized state.

Edit: This part of the comment was irrelevant.

Here's how I managed to take both scenarios into account:
// src/stores/index.js

import { store as createStore } from 'quasar/wrappers';
import { createPinia } from 'pinia';
import { createPersistedState } from 'pinia-plugin-persistedstate';

export default createStore((/* { ssrContext } */) => {
  const pinia = createPinia();

  if (!import.meta.env.SSR) {
    pinia.use(createPersistedState({
      auto: true,
      beforeRestore: ({ store }) => {
        if (store.$state.hasChanged === true) {
          store.$persist();
        }
      },
    }));
  }

  return pinia;
});
// src/stores/example.js

import { defineStore } from 'pinia';

export const useExampleStore = defineStore('example', {
  state: () => ({
    counter: 0,
    data: null,
    hasChanged: false,
  }),
  getters: {
    doubleCount: (state) => state.counter * 2,
  },
  actions: {
    increment() {
      this.counter += 1;
      this.hasChanged = true;
    },
    async fetchData() {
      this.data = await (await fetch('https://someapi.com')).json();
      this.hasChanged = true;
    },
  },
});
// src/components/ExampleComponent.vue

<template>
  <div>
    <div>Direct store</div>
    <!-- Read the state value directly -->
    <div>{{ store.counter }}</div>
    <pre>{{ store.data }}</pre>
    <!-- Use getter directly -->
    <div>{{ store.doubleCount }}</div>


    <!-- Manipulate state directly -->
    <q-btn @click="store.counter--">-</q-btn>
    <!-- Use an action -->
    <q-btn @click="store.increment()">+</q-btn>
  </div>

</template>

<script>
import { useExampleStore } from 'stores/example';
import { onBeforeMount, onServerPrefetch } from 'vue';

export default {
  setup() {
    const store = useExampleStore();

    // this hook is executed at server side only
    onServerPrefetch(async () => {
      await store.fetchData();
    });

    // this hook is executed at client-side only
    onBeforeMount(async () => {
      if (store.hasChanged === false) {
        await store.fetchData();
      }
    });

    return {
      store,
    };
  },
};
</script>

@freddy38510
Copy link
Owner

The use of ssg/ssr and client-side persistent state leads to two distinct sources of state.

You have one source initialized at build-time, and an other from a storage at client-side.

If these two sources differ, there will be a client-side warning about the hydration mismatch.

You can enable the Quasar manualStoreHydration option, but you'll still have hydration mismatch. This is because the pre-rendered content will have the initial state, which may differ from the persistent state.

There are two ways of solving this problem:

  • The simplest is to use the QNoSsr component.
  • Alternatively, you can try making the pinia persisted state plugin run after hydration is complete.

The disadvantage of these two solutions is that you'll have to manage a kind of loading state, rather than having content that changes abruptly.

For the second solution, I found a not-so-clean way of doing it by using the 'onMounted' hook in the root component "App.vue":

// src/App.vue

<template>
  <router-view />
</template>

<script setup>
import { getActivePinia } from 'pinia';
import { createPersistedState } from 'pinia-plugin-persistedstate';
import { onMounted } from 'vue';

if (process.env.CLIENT) {
  const pinia = getActivePinia();

  pinia?.use((context) => {
    onMounted(() => {
      createPersistedState({
        auto: true,
        beforeRestore: () => {
          console.log('beforeRestore');
        },
        afterRestore: () => {
          console.log('afterRestore');
        },
      })(context);
    });
  });
}
</script>
// src/store/index.js

import { store as createStore } from 'quasar/wrappers';
import { createPinia } from 'pinia';

export default createStore(() => createPinia());

@freddy38510 freddy38510 added the question Further information is requested label Jul 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants