Nuxt.js: Persistedstate LocalStorage not work.

Created on 15 Mar 2018  ·  9Comments  ·  Source: nuxt/nuxt.js

Can not figure out and find right solution. Everything not work.

After authentication, i need to save data not only to Store and to localStorage, but also synchronize everything. That after page reload, user will be authed.

nuxt.conf

    plugins: [
        { src: '~/plugins/localStorage', ssr: false }
    ],

localStorage.js

import createPersistedState from 'vuex-persistedstate'

export default ({store}) => {
    createPersistedState({
        storage: {
            getItem: key => localStorage.getItem(key),
            setItem: (key, value) => localStorage.setItem(key, value),
            removeItem: key => localStorage.removeItem(key)
        }
    })
}

example.vue component

    import MyPlugin from '~/plugins/localStorage'

    export default {
        created() {
            console.log(MyPlugin);
            console.log(MyPlugin.local);
            console.log(MyPlugin.setting);
//            console.log(MyPlugin.storage.getItem('token'));
//            console.log(MyPlugin.getItem('token'));

            console.log(this.$store.local);
            console.log(this.$store.setting);
        },

Nothing works
`[HMR] connected
form.vue?385a:89 ƒ (_ref) {
var store = _ref.store;

Object(__WEBPACK_IMPORTED_MODULE_0_vuex_persistedstate__["a" /* default */])({
    storage: {
        getItem: function getItem(key) {
          …

form.vue?385a:90 undefined
form.vue?385a:91 undefined
form.vue?385a:95 undefined
form.vue?385a:96 undefined`

This question is available on Nuxt.js community (#c2625)

Most helpful comment

Maybe it can help you, when I declared my plugin like you, I had some issues with vue-persistedstate, but with window.onNuxtReady(...) it works fine 👍

// plugins/persistedstate.js

import createPersistedState from 'vuex-persistedstate'

export default ({store, isHMR}) => {
  if (isHMR) return

  window.onNuxtReady(() => {
    createPersistedState({
      key: 'nuxtweather',
      paths: ['city', 'cities']
    })(store)
  })
}

full source code here: https://github.com/NicoPennec/nuxt-weather

All 9 comments

Okay i figured out now. It is working, BUT, when component is mounted and user is Logged, he sees Login navigation link, after that we update our Vuex Store from localStorage, and replace LogIn link in navigation with LogOut. Because it is linked like this

<li>
                            <a v-if="isAuthenticated" @click="logOut">
                                Log Out
                            </a>
                        </li>
                        <li>
                            <nuxt-link v-if="!isAuthenticated"
                                       to="login"
                                       title="Login"
                            >
                                Login
                            </nuxt-link>
                        </li>
                        <li>
                            <nuxt-link v-if="!isAuthenticated"
                                       to="registration"
                                       title="Register"
                            >
                                Register
                            </nuxt-link>
                        </li>

...
export default {
        computed: mapGetters(['isAuthenticated']),
        methods: {
            logOut() {
                    this.$store.dispatch('logOutUser').then(() => this.$router.replace({path: `/`}));
            },
        },
    }

So user can see Login link and other for not authenticated users, and after we update Store data, it replaced with data for authenticated users.

Tell me please somebody, how to avoid this? What we are doing wrong?

Also, we do not want to use cookies for api, because we will need CSRF protection that cause a lot of redundant actions to be done.

Maybe it can help you, when I declared my plugin like you, I had some issues with vue-persistedstate, but with window.onNuxtReady(...) it works fine 👍

// plugins/persistedstate.js

import createPersistedState from 'vuex-persistedstate'

export default ({store, isHMR}) => {
  if (isHMR) return

  window.onNuxtReady(() => {
    createPersistedState({
      key: 'nuxtweather',
      paths: ['city', 'cities']
    })(store)
  })
}

full source code here: https://github.com/NicoPennec/nuxt-weather

@NicoPennec we have at the moment this

import createPersistedState from 'vuex-persistedstate'

export default ({store, isHMR, isClient}) => {
    if (isHMR) return;
    if (isClient) {
        window.onNuxtReady((nuxt) => {
            createPersistedState({
                storage: {
                    getItem: key => localStorage.getItem(key),
                    setItem: (key, value) => localStorage.setItem(key, value),
                    removeItem: key => localStorage.removeItem(key)
                }
            })(store); // vuex plugins can be connected to store, even after creation
        })
    }
}

and configs

module.exports = {
    /**
     * Router config
     */
    router: {
        middleware: 'check-auth'
    },
    /**
     * Headers of the page
     */
    head: {
        title: 'Test',
        meta: [
            {charset: 'utf-8'},
            {name: 'viewport', content: 'width=device-width, initial-scale=1'},
            {hid: 'description', name: 'description', content: '{{escape description }}'}
            // { rel: 'apple-touch-icon', href: '/icon/icon-152.png' },
        ],
        link: [
            {rel: 'icon', type: 'image/x-icon', href: '/favicon.ico'},
            {rel: 'stylesheet', href: 'http://fonts.googleapis.com/css?family=Lato:300,400,700,900'},
        ],
    },

    /**
     * Rendered CSS files content directly in DOM
     */
    css: [
        {src: '@assets/css/foundation.css', lang: 'scss'},
    ],

    /**
     * Nuxt Modules, rendered directly in DOM
     */
    modules: [
        '@nuxtjs/axios',
        '@nuxtjs/font-awesome',
        ['@nuxtjs/component-cache', {maxAge: 1000 * 60 * 60}],
    ],

    /**
     * Customize the progress bar color
     */
    loading: {color: '#3B8070'},

    /**
     * Build configuration
     */
    build: {
        vendor: ['axios', 'babel-polyfill', 'vuex-persistedstate'],
        /**
         * Run ESLint on save
         */
        extend(config, {isDev, isClient}) {
            // extend webpack configs here
            if (isDev && isClient) {
                config.module.rules.push({
                    enforce: 'pre',
                    test: /\.(js|vue)$/,
                    loader: 'eslint-loader',
                    exclude: /(node_modules)/
                })
            }
        },
    },

    /**
     * Plugins
     */
    plugins: [
        {src: '~/plugins/localStorage', ssr: false}
    ],
};

Maybe i do not understand how key and path works in createPersistedState(), and this is an issue.

Or, as i can see from comments here, it seems Nuxt still have issues with storing persisted data
https://github.com/robinvdvleuten/vuex-persistedstate/issues/54

Also, here i found
https://github.com/championswimmer/vuex-persist
from discussion
https://stackoverflow.com/questions/44126493/i-want-to-use-window-localstorage-in-vuex-in-nuxt-js
Have anyone tried it to use with nuxt?

Have changed currently to sync everything to cookies, but this is bad solition for API and JWT, because we are open for CSRF but this is kludge

As discussed here, we might need a better suited hook than onNuxtReady to rehydrate the state.

It does work in some cases, but it happens way too late in the lifecycle, so it

  • affects the user experience with a short flash at loading

  • kinda overrides pages lifecycle hooks fetch/mounted/..

@Atinux any plans of addressing this? I can't see where could we possibly speed up onNuxtReady

@fagacil JWT is a powerful token. It's better not store it in localStorage/sessionStorage. Have a look at the reasons here. A better solution is to make sure your JWT token is generated server-side (Koa, Express, PHP, Python, Lua, ...) as an HttpOnly cookie. That JWT token can easily be unpacked (if signature is still valid) and also frequently re-validated as an off-the-band request from where you already get them.

@galvez you don't need to have it on onNuxtReady, you can have it plainly as a non-SSR plugin. Recent releases proven to work well for me, the problem noted at the creation of this issue seems solved.

For the record, I have exactly the same as what @fagacil has, but shown below with comments and observations.

// ~/plugins/localStorage
// Same as @fagcil (with notes)

import createPersistedState from 'vuex-persistedstate'

export default async ({
  app, store, $axios, isHMR,
}) => {
  // Just in case nuxt.config.js gets a change in ssr:false when adding this plugin.
  // Notice isClient is deprecated, instead use process.browser. 
  if (process.browser) {
    // In case of HMR, mutation occurs before nuxtReady, so previously saved state
    // gets replaced with original state received from server. So, we've to skip HMR.
    // Also nuxtReady event fires for HMR as well, which results multiple registration of
    // vuex-persistedstate plugin
    if (isHMR) return
    createPersistedState({
      key: 'myapp',

      // An array of any paths to partially persist the state.
      // Paths are using dots to indicate nestedness.
      paths: [
        'menu.hidden',
        'localization.locale',
      ],
    })(store) // vuex plugins can be connected to store, even after creation
  }
}

... In the hopes my comments were useful.

Thank you so much for chiming in, @renoirb! -- I'm closing this issue now.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaredreich picture jaredreich  ·  3Comments

uptownhr picture uptownhr  ·  3Comments

o-alexandrov picture o-alexandrov  ·  3Comments

bimohxh picture bimohxh  ·  3Comments

danieloprado picture danieloprado  ·  3Comments