'Why does calling "useUserStore(store)" in a Quasar boot file breaks the Pinia Persistedstate Plugin?

I'm trying to use the Pinia Persisted State Plugin with Pinia in my Quasar app (Vue 3 / TypeScript).

Out of the box everything works fine.

But when using a Quasar boot file the persisted state stops working. Refreshing the page wipes all the new values away.

I don't know why the boot file breaks the persisted state plugin, but I have narrowed the culprit down to a single line...

This is how I am using Pinia with Quasar and adding the plugin:

src/store/index.ts

/* eslint-disable @typescript-eslint/no-unused-vars */
import { store } from 'quasar/wrappers';
import { createPinia, Pinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';

declare module '@quasar/app' {
  interface BootFileParams<TState> {
    store: Pinia;
  }
  interface PreFetchOptions<TState> {
    store: Pinia;
  }
}

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $store: import('pinia').Pinia;
  }
}

export default store(function (_) {
  const pinia = createPinia();
  pinia.use(piniaPluginPersistedstate); // Pinia Plugin added here
  return pinia;
});

And this is what my Pinia store looks like:

src/store/user.ts

import { defineStore } from 'pinia';

export const useUserStore = defineStore('user', {
  state: () => {
    return {
      user: {
        firstName: 'Mary',
      },
    };
  },
  persist: true, // Note that we are using a persisted state here
  actions: {
    setFirstName(firstName: string) {
      this.user.firstName = firstName;
      console.log('Name set to Pinia store: ', this.user.firstName);
    },
    getFirstName() {
      if (!this.user.firstName) {
        console.log('No name found in store. Setting "John" to Pinia store.');
        this.user.firstName = 'John';
        return this.user.firstName;
      } else {
        console.log('Name fetched from Pinia store: ', this.user.firstName);
        return this.user.firstName;
      }
    },
  },
});

Here is an example front-end page for fetching and setting the firstName:

src/pages/index.vue

<template>
  <div>{{ firstName }}</div>
  <q-form @submit="handleFirstNameSubmit">
    <p>Change First Name</p>
    <q-input v-model="firstNameInput" filled outline />
    <q-btn label="Submit Name to Pinia Store" type="submit" />
  </q-form>
  <q-btn @click="handleFirstNameFetch" label="Fetch Name from Pinia Store" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useUserStore } from 'src/store/user';
const userStore = useUserStore();
const firstName = ref<string>();
const firstNameInput = ref<string>();
const handleFirstNameSubmit = () => {
  if (firstNameInput.value) {
    userStore.setFirstName(firstNameInput.value);
  }
};
const handleFirstNameFetch = () => {
  firstName.value = userStore.getFirstName();
};
</script>

Up to this point everything works fine.

I can set firstName to the Pinia store, refresh the page, and the new name is still in Pinia.

But when using const userStore = useUserStore(store) inside a boot file like the example below, the persisted state stops working:

src/boot/auth.ts

import { boot } from 'quasar/wrappers';
import { useUserStore } from 'src/store/user';

export default boot(({ store }) => {
  const userStore = useUserStore(store);
  // Do some other authentication stuff, setting initial user store values etc, below here...
});

Any idea what's going on? And how to fix it?

I think this plugin is much cleaner than using the alternate LocalStorage persisted state solution so I would love to get it working with Quasar.



Solution 1:[1]

A common use case for Quasar applications is to run code before the root Vue app instance is instantiated.

If the app is not instantiated then the pinia plugin hasn't been installed yet. See: https://github.com/vuejs/pinia/discussions/723#discussioncomment-2110660

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Thomas Guntenaar