Open tamysiby opened 1 year ago
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>
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 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());
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!