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

[nuxt] Pinia State doesn't persist when set from middleware #221

Open
4 tasks done
d0peCode opened this issue Jul 16, 2023 · 24 comments
Open
4 tasks done

[nuxt] Pinia State doesn't persist when set from middleware #221

d0peCode opened this issue Jul 16, 2023 · 24 comments
Labels
✨ enhancement New feature or request ✋ help wanted Extra attention is needed

Comments

@d0peCode
Copy link

Describe the bug

This is my Pinia module:

import { defineStore } from "pinia";
export const useStore = defineStore("store", {
    state: () => ({
        token: '',
    }),

    actions: {
        setToken(token: string) {
            this.token = token;
        }
    },

    persist: true
});

I set the state with setToken function from the middleware

import {useStore} from "~/store/useStore";

export default defineNuxtRouteMiddleware(() => {
    const store = useStore();
    console.log('store.token1', store.token)
    store.setToken('token')
    console.log('store.token2', store.token)
});

Now on first app reload I would expect to see logs:

store.token1
store.token2 token

and on the second reload I would expect to see

store.token1 token
store.token2 token

Instead I see:

image

Reproduction

https://github.com/d0peCode/nuxt3-pinia-middleware-issue

System Info

MacOS, Chrome

Used Package Manager

npm

Validations

@d0peCode d0peCode added the 🔍️ pending triage This issue needs to be looked into label Jul 16, 2023
@MZ-Dlovely
Copy link
Contributor

maybe you're reading log from your terminal? when server render, console.log while print at terminal and there is no localstorage. may you can watch the console on chrome

@d0peCode
Copy link
Author

maybe you're reading log from your terminal? when server render, console.log while print at terminal and there is no localstorage. may you can watch the console on chrome

Doesn't it work with cookies? I thought it is SSR friendly and I can read persisted values from store in my middleware.

@MZ-Dlovely
Copy link
Contributor

MZ-Dlovely commented Jul 17, 2023

maybe you're reading log from your terminal? when server render, console.log while print at terminal and there is no localstorage. may you can watch the console on chrome

Doesn't it work with cookies? I thought it is SSR friendly and I can read persisted values from store in my middleware.

when first run app and enter page, this route middleware will trigger immediately. store will be Initialized and token will be changed. let's see our plugin. it ready to use storage, but it will check useNuxtApp().ssrContext which is not instantiated this time. so nuxt say A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function.

@MZ-Dlovely
Copy link
Contributor

you can try to run getCurrentInstance() in route middleware and print its result. useNuxtApp mainly obtain nuxt app through getCurrentInstance()?.appContext.app.$nuxt

@d0peCode
Copy link
Author

I'm changing the state of pinia module from the middleware which suppose to have access to nuxt instance.

I thought that the change to pinia store which I made from the middleware would persist with your plugin.

From middleware I execute function which change pinia store. Cookie is not created by pinia-plugin-persistedstate. Look at reproduction repository.

@MZ-Dlovely
Copy link
Contributor

maybe we can change the judgment method, like:

function usePersistedstateSessionStorage() {
  return ({
    getItem: (key) => {
      return checkWindowsKey('sessionStorage')
        ? sessionStorage.getItem(key)
        : null
    },
    setItem: (key, value) => {
      if (checkWindowsKey('sessionStorage'))
        sessionStorage.setItem(key, value)
    },
  }) as StorageLike
}

function checkWindowsKey(key: string) {
  return process.dev && key in window
}

how do you think about it? @prazdevs

@d0peCode
Copy link
Author

d0peCode commented Jul 17, 2023

maybe we can change the judgment method, like:

function usePersistedstateSessionStorage() {
  return ({
    getItem: (key) => {
      return checkWindowsKey('sessionStorage')
        ? sessionStorage.getItem(key)
        : null
    },
    setItem: (key, value) => {
      if (checkWindowsKey('sessionStorage'))
        sessionStorage.setItem(key, value)
    },
  }) as StorageLike
}

function checkWindowsKey(key: string) {
  return process.dev && key in window
}

how do you think about it? @prazdevs

I thought Cookies is default but even when I changed my store to:

import { defineStore } from "pinia";
export const useStore = defineStore("store", {
    state: () => ({
        token: '',
    }),

    actions: {
        setToken(token: string) {
            this.token = token;
        }
    },

    persist: {
        storage: persistedState.cookiesWithOptions({
            sameSite: 'strict',
        }),
    },
});

It also doesn't work:
image


EDIT: sorry I accidentally pasted wrong image previously thus edit

@d0peCode
Copy link
Author

Also cookie is not created:
image

@MZ-Dlovely
Copy link
Contributor

whether it's localstorage or cookie, it will all use useNuxtApp. you can use object like { debug: true } to replace true, then you can see the stacks about the error.I think I described the reason for the mistake in the previous two consecutive comments.

@d0peCode
Copy link
Author

So your plugin doesn't make pinia state persist if you set state from the server side of nuxt app lifecycle?

@MZ-Dlovely
Copy link
Contributor

sure, if you change the state at server side, how we know you have changed when we stand on client side. about share data between server and client, nuxt3 suggests using useState.
of course, we can require owner of plugin to improve and perfect persistedState. it may be support to use useState.

@MZ-Dlovely
Copy link
Contributor

if you wang to persist at client storage after server side changed, we can try to do. but if you want to read value from client storage when server render, it's impossible, because there is no connection to the client side even though itself nuxt3.

@prazdevs prazdevs changed the title Nuxt Pinia State doesn't persist when set from middleware [nuxt] Pinia State doesn't persist when set from middleware Jul 23, 2023
@Ena-Heleneto
Copy link

you can do this

export default defineNuxtRouteMiddleware(to => {
  // skip middleware on server
  if (process.server) return
  // skip middleware on client side entirely
  if (process.client) return
  // or only skip middleware on initial client load
  const nuxtApp = useNuxtApp()
  if (process.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
})

https://nuxt.com/docs/guide/directory-structure/middleware

@d0peCode
Copy link
Author

sure, if you change the state at server side, how we know you have changed when we stand on client side. about share data between server and client, nuxt3 suggests using useState. of course, we can require owner of plugin to improve and perfect persistedState. it may be support to use useState.

You could check the changes in pinia in nuxt server side hooks and create server side cookie to then hydrate on client. There is many possibilities to achieve real SSR-friendly persistent state.

If you don't support making changes in pinia store from the server side and don't persist those changes then [pinia-plugin-persistedstate] is not SSR friendly and you should mention it in docs

@MZ-Dlovely
Copy link
Contributor

You could check the changes in pinia in nuxt server side hooks and create server side cookie to then hydrate on client. There is many possibilities to achieve real SSR-friendly persistent state.

If you don't support making changes in pinia store from the server side and don't persist those changes then [pinia-plugin-persistedstate] is not SSR friendly and you should mention it in docs

if you think we are not SSR-friendly because of we cannot persist client data on server side, just like asking me to count how many lights there are in your house. when you first visited me, I didn't even know who you were, let alone let me find your house.

@d0peCode
Copy link
Author

d0peCode commented Jul 26, 2023

if you think we are not SSR-friendly because of we cannot persist client data on server side

Your library is working on client side only. With Nuxt you can load any javascript package on client side only. Going with your logic everything anyone has ever wrote in javascript is SSR friendly. :)

SSR in Nuxt gives you entire server side lifecycle. You could hook up function at the end of server lifecycle just before app is sent to browser. In this function you could create a cookie using h3 library with current pinia state.

Then in the browser you could read this cookie, compare and detect changes and apply them to the pinia.

This way every change you've made on server to your pinia module - in server plugin, middleware or whatever - would persist and your library would be SSR friendly.

@prazdevs
Copy link
Owner

prazdevs commented Jul 26, 2023

PRs are always welcome :)

That being said, keep in mind the nuxt module is an implementation of the base plugin, to work easily with Nuxt, and for most uses. Not everyone uses middleware and modify pinia stores in there.

So, yes, the library is SSR friendly with most use cases.

There are lots of cases that could be improved on the nuxt part, but the nuxt implementation is very simple. Keep in mind most of it is my work, on my free time, over nights, so I'd ask to stay respectful, for me and everyone who has contributed so far.

SSR is a very complex topic, and Nuxt still changes a lot. Keeping server and client in sync is ridiculously difficult, let alone middleware or server components... The Nuxt module was created when Nuxt3 was released officially, and docs were not even complete. Nuxt module docs are still not complete, and unit testing is still in RFC!

tl;dr: be respectful. the module fulfils most people's needs (ssr included) and there will be improvement eventually.

also thanks @Abernethy-BY & @MZ-Dlovely for the answers 👍

@MZ-Dlovely
Copy link
Contributor

I'm sorry that my thought is wrong before you explained. but the good news is that I have an idea, and I'm trying to solve it tomorrow.

@MZ-Dlovely
Copy link
Contributor

I have done a lot of stupid things. :(
after trying several possibilities, I found that just using it store.$persist() is enough.
just like your code @d0peCode :

import {useStore} from "~/store/useStore";

export default defineNuxtRouteMiddleware(() => {
    const store = useStore();
    console.log('store.token1', store.token)
    store.setToken('token')
    console.log('store.token2', store.token)
    // just do this
    preset_cookie.$persist()
});

@MZ-Dlovely
Copy link
Contributor

if you dont wang to use it by yourself, i can write some thing to make it automatic

@prazdevs prazdevs added ✨ enhancement New feature or request 🔍️ pending triage This issue needs to be looked into and removed 🔍️ pending triage This issue needs to be looked into labels Sep 5, 2023
@erdemyunsel
Copy link

erdemyunsel commented Nov 22, 2023

Same on me. I cant get persist states in middleware after refresh page.

import { useMainStore } from "~/store";

export default defineNuxtRouteMiddleware(async (to, from) => {
  const nuxtApp = useNuxtApp()
  
  const store =  useMainStore();
  const config = useRuntimeConfig();

  const authRequiredPages = ["/panel", "/teklif-al"];
  // ? if user is not logged in and to.path.startsWith authRequiredPages
  const isLoginRequired = authRequiredPages.some((authPaths) => {
    if (to.path.startsWith(authPaths)) {
      return true;
    }
  });

  if (isLoginRequired) {
    const { data: sessionControl } = await useFetch(
      `${config.public.API_URL}users/session`,
      {
        method: "GET",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${store.token}`,
        },
      }
    );
    if (sessionControl?.value?.user) {
      store.setUser(sessionControl.value.user);
      store.setToken(sessionControl.value.token);
    } else {
      store.logout();
      return navigateTo("/giris");
    }
  }else if(isLoginRequired && !store.token){
    return navigateTo("/giris");
  }

  
});

@MZ-Dlovely
Copy link
Contributor

yep!(clap hands

@erdemyunsel
Copy link

Now its okay with adding middleware to this.

  if (process.server) {
    return
  }

Now middleware.


...
export default defineNuxtRouteMiddleware(async (to, from) => {
  const nuxtApp = useNuxtApp()
  
  const store =  useMainStore();
  const config = useRuntimeConfig();
  
  if (process.server) {
    return
  }

  const authRequiredPages = ["/panel", "/teklif-al"];
  // ? if user is not logged in and to.path.startsWith authRequiredPages
  const isLoginRequired = authRequiredPages.some((authPaths) => {
    if (to.path.startsWith(authPaths)) {
      return true;
    }
  });

....

@prazdevs prazdevs added ✋ help wanted Extra attention is needed and removed 🔍️ pending triage This issue needs to be looked into labels Dec 16, 2023
@RomainMazB
Copy link

I have done a lot of stupid things. :( after trying several possibilities, I found that just using it store.$persist() is enough. just like your code @d0peCode :

import {useStore} from "~/store/useStore";

export default defineNuxtRouteMiddleware(() => {
    const store = useStore();
    console.log('store.token1', store.token)
    store.setToken('token')
    console.log('store.token2', store.token)
    // just do this
    preset_cookie.$persist()
});

This was the missing piece, thanks!!

I'm using a check-auth.global.ts middleware to refresh and store the session token and faced this issue as well.

Just adding the $persist method after refreshing the token fixed my issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ enhancement New feature or request ✋ help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

6 participants