GithubHelp home page GithubHelp logo

Comments (44)

zweizeichen avatar zweizeichen commented on August 12, 2024 59

EDIT: this solution comes with a caveat - see comment below (This package now uses deepmerge for merging state.)

Okay I think I have found a solution, however I am not sure if I have fully understood what actually happens. As I wrote in nuxt/nuxt#1641, if you try to load state later in Vue's lifecycle, it will already have been overwritten by state supplied by Nuxt. This is because you're supplying the store plugin (vuex-persistedstate) directly during the store's creation. Nuxt will then call replaceState during initialization in your generated server.js. That way the store plugin will overwrite the client's localStorage with the state supplied by the server and write that state back into localStorage, effectively overwriting existing changes with the default state returned by the server. Now what we can do is deferring the attachment of vuex-persistedstate to our store instance. That way the state will be already replaced when the plugin is loaded. We're lucky that Nuxt plugins are loaded after the state has been replaced, so we can place that code inside a Nuxt plugin. We can load a plugin into vuex by performing the actions the constructor would normally do: https://github.com/vuejs/vuex/blob/84a503e29387e975fde353b0e3703065ceb6884e/dist/vuex.js#L374-L378

All we have to do is writing a client-side Nuxt plugin which loads vuex-persistedstate:

// nuxt.config.js

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

import createPersistedState from 'vuex-persistedstate'

export default ({store}) => {
  createPersistedState({
      key: 'yourkey',
      paths: [...]
  })(store)
}

I hope this is the right way to do this. Please try it yourself. Someone who is more familiar with Nuxt/vuex etc. should review this solution, feedback would be greatly appreciated.

from vuex-persistedstate.

vladislav-sevostyanov avatar vladislav-sevostyanov commented on August 12, 2024 14
import createPersistedState from 'vuex-persistedstate';
import * as Cookies from "js-cookie";

let cookieStorage = {
  getItem: function(key) {
    return Cookies.getJSON(key);
  },
  setItem: function(key, value) {
    return Cookies.set(key, value, {expires: 3, secure: false});
  },
  removeItem: function(key) {
    return Cookies.remove(key);
  }
};

export default (context) => {
  createPersistedState({
    storage: cookieStorage,
    getState: cookieStorage.getItem,
    setState: cookieStorage.setItem
  })(context.store);
};


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

It works for me.

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024 8

I just discovered a caveat in my solution. vuex-persistedstate uses lodash's merge function when seeding the state from storage. Suppose we have a list of items in our default state, if the user deletes an item from that list, that item will be re-added on next load as the array gets merged with the default state. This behaviour can be useful when adding new keys later on, however it interferes with your app's logic when you try to seed some useful values which the user can customize later on.

from vuex-persistedstate.

mtnptrsn avatar mtnptrsn commented on August 12, 2024 7

Changed to this:

plugins: [
     createPersistedState({
         paths: ['counter'],
         storage: {
             getItem: (key) => Cookie.getJSON(key),
             setItem: (key, value) => Cookie.set(key, value, { expires: 3, secure: true }),
             removeItem: (key) => Cookie.remove(key)
         }
     })
]

Still doesn't work.

from vuex-persistedstate.

mtnptrsn avatar mtnptrsn commented on August 12, 2024 7

@dnomak Yeah i've seen that too. But that is using mode: 'spa' which is not what I want. I literally use nuxt because because of ssr.

from vuex-persistedstate.

robinvdvleuten avatar robinvdvleuten commented on August 12, 2024 7

I've created a jsfiddle with the plugin configured to use a cookie storage. Please see the README for the link.

from vuex-persistedstate.

ishitatsuyuki avatar ishitatsuyuki commented on August 12, 2024 7

Check out this solution: nuxt/nuxt#972 (comment)

from vuex-persistedstate.

dnomak avatar dnomak commented on August 12, 2024 5

I have the same problem

from vuex-persistedstate.

dnomak avatar dnomak commented on August 12, 2024 4

is there another in this example @Atinux https://github.com/nuxt/nuxt.js/tree/ef4cdd477644cfb4571e1d7eb3b9ef1d16a3ed54/examples/vuex-persistedstate this example is pointless :(

from vuex-persistedstate.

outOFFspace avatar outOFFspace commented on August 12, 2024 3

Here is a little workaround, if you don't need to use persisted state on server side:

  const plugins = []
  if (process.env.VUE_ENV === 'client') {
    plugins.push(createPersistedState({
      storage: window.localStorage
    }))
  }
  return new Vuex.Store({
    state: {
      count: 0
    },
    plugins,
    mutations: {
      increment: state => state.count++,
      decrement: state => state.count--
    }
  })

from vuex-persistedstate.

lbba avatar lbba commented on August 12, 2024 3

customize-storage sample sets cookie, but not load cookie to vuex
(https://github.com/robinvdvleuten/vuex-persistedstate#customize-storage)

when using not spa, I loaded it manually

nuxtServerInit ({ commit, dispatch }, { req }) {
    if (req.headers.cookie) {
      dispatch('setState', parse(req.headers.cookie))
    }
  }
}

from vuex-persistedstate.

syffs avatar syffs commented on August 12, 2024 3

@zweizeichen @robinvdvleuten I'm facing several issues using the nuxt guide #125.

  • reactvity
  • SSR related, like client-side rendered virtual DOM tree is not matching server-rendered content... or
[nuxt] Error while initializing app TypeError: Cannot read property 'toLowerCase' of undefined
    at emptyNodeAt (vue.runtime.esm.js:5487)

I can fix these by using onNuxtReady.

I then face other issues either when my middleware pops in to redirect the user to the 1st page of my flow when the store is empty or when my page cannot access the store data and crashes.

I think onNuxtReady is not ideal as it happens way too late in the lifecycle. Ideally, we'd need a hook on store creation in the nuxt lifecycle to rehydrate the state.

Any suggestion?

from vuex-persistedstate.

vedmant avatar vedmant commented on August 12, 2024 3

For me only @lbba solution worked where I restored state with nuxtServerInit manually. vuex-persistedstate does run getItem on server side but state still not restored when used in plugins. State was available in plugins only after I restored it with nuxtServerInit and committed to the store.

  nuxtServerInit ({ commit, dispatch }, { req }) {
    if (req.headers.cookie) {
      try {
        const state = JSON.parse(parseCookies(req)['vuex'])
        commit('SET_STATE', state)
      } catch (e) { }
    }
  },
  SET_STATE (state, newState) {
    Object.assign(state, newState)
  },

from vuex-persistedstate.

dnomak avatar dnomak commented on August 12, 2024 2

help us 😇

from vuex-persistedstate.

jpbamberg1993 avatar jpbamberg1993 commented on August 12, 2024 2

@vladislav-sevostyanov you are using js-cookie but have ssr set to false... why not just use localStorage? Isn't the only reason to use js-cookie for server side?

from vuex-persistedstate.

mtnptrsn avatar mtnptrsn commented on August 12, 2024 1

@dnomak Oh all right, thanks!

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024 1

While this solution works fine for smaller fragments of state IMHO there are some problems if we want to store more state:

  • There is a size limit for cookies
  • Passing that state back and forth in every subsequent request (think API requests to the same domain) is not that elegant

Maybe it would be better if we could find a solution which simply 'ignores' server-side state for paths managed by vuex-persistedstate.

from vuex-persistedstate.

aliir74 avatar aliir74 commented on August 12, 2024 1

Hi @zweizeichen! Does we need write anything about createPersistedState in store.js? I have problem to using your solution.

from vuex-persistedstate.

Zazck avatar Zazck commented on August 12, 2024 1

@amritk I can't understand your solution, I don't know where those code should be placed. But I found another dirty solution. I don't know why these code works

// ~/plugins/localStorage.js
import createPersistedState from 'vuex-persistedstate'

export default ({store}) => {
  // why this work? is this workaround elegant?
  store.commit('any_mutation', store)

  createPersistedState({
      key: 'setting',
      paths: ['local']
  })(store)
}

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024 1

I checked the code again and it seems this caveat does not apply anymore. A couple of months ago it used lodash's merge function, however nowadays deepmerge is used which simply overwrites arrays (#89):

arrayMerge: function (store, saved) { return saved },

The original problem was if a key in your initital state was set to something like [1, 2, 3] and the user removed numbers your state would read something like [1, 3]. On reload this would get merged with the default state again thus leading to e.g. [1, 3, 2]. Now the user-supplied state in arrays always takes precedence (see the arrayMerge function).

To cut a long story short, you should not need to worry about this at all, I missed that @robinvdvleuten updated the merge function.

from vuex-persistedstate.

nikugogoi avatar nikugogoi commented on August 12, 2024 1

@zweizeichen finally can i rest in peace 😅 ... thanks for putting your time into it

from vuex-persistedstate.

robinvdvleuten avatar robinvdvleuten commented on August 12, 2024

@mtnptrsn It looks like you've included the paths under the storage key. These should be in the root of your options object. Could you try that again please?

from vuex-persistedstate.

dnomak avatar dnomak commented on August 12, 2024

that package can recover state https://github.com/alfhen/vue-cookie it is not very true but it does work @mtnptrsn

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024

Please also see nuxt/nuxt#1641

from vuex-persistedstate.

robinvdvleuten avatar robinvdvleuten commented on August 12, 2024

I'll check if I can do a quick jsfiddle on this. It should work just as the example 👍

from vuex-persistedstate.

gridsystem avatar gridsystem commented on August 12, 2024

Bloody brilliant thanks @zweizeichen

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024

Hi, there is no createPersistedState in store.js as it is handled by the Nuxt plugin. You might want to check if there are reactivity issues in your components which could cause not seeing the updated data loaded from localStorage. The Vue browser plugins can also help debugging such issues.

from vuex-persistedstate.

gridsystem avatar gridsystem commented on August 12, 2024

@zweizeichen I've just experienced this issue too and found your comment. I'm not sure what to do about this other than manually filling my list with a mutation instead of having a default state, which is not ideal.

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024

We could make the merge function or paths where client state takes precedence over server state configurable - what do you think @robinvdvleuten?

from vuex-persistedstate.

robinvdvleuten avatar robinvdvleuten commented on August 12, 2024

@zweizeichen @gridsystem I do not have that much experience with Nuxt.js myself. It would be very awesome if one of you guys can help me fix it through a PR! I got a lot of issues with Nuxt.js on the repository so all help is welcome!

from vuex-persistedstate.

fidellr avatar fidellr commented on August 12, 2024

@zweizeichen hey i just used your solution, it worked well but before the user signed in, the sign in button will appeared and after the user is signed in the sign in button will be change to be like users profile picture, and when i refresh it my users profile picture will be dimmed/changed to sign in button and changed it again into profile picture again, if theres a solution i'll appreciate tht! thx in advance

from vuex-persistedstate.

amritk avatar amritk commented on August 12, 2024

Haveing the same issue, just with standard SSR as opposed to Nuxt.js

from vuex-persistedstate.

Zazck avatar Zazck commented on August 12, 2024

@fidellr here's a quick & dirty solution, put <no-ssr> on your <nuxt />, or put on component that requires localstorage.
or it's not dirty because this is user-side data?
example (lang="pug"):

no-ssr
  v-avatar(:tile="$store.state.local.ui_avatar_tile")

or wrap on <nuxt />

|-- layouts/default.vue
no-ssr
  nuxt/

from vuex-persistedstate.

amritk avatar amritk commented on August 12, 2024

My little hack was when you hydrate the client, grab certain objects from local storage that you want to persist:

const persisted = ['location']
let replace = {}
if (window.__INITIAL_STATE__) {
    persisted.forEach(key => {
        replace[key] = store.state[key]
    })
    store.replaceState(Object.assign({}, window.__INITIAL_STATE__, replace))
}

from vuex-persistedstate.

amritk avatar amritk commented on August 12, 2024

@outOFFspace I found with that workaround, the server state was overwriting the persisted client state

from vuex-persistedstate.

amritk avatar amritk commented on August 12, 2024

@Zazck it is where you load your server state into your client state. For me it was in entry-client.js

from vuex-persistedstate.

nikugogoi avatar nikugogoi commented on August 12, 2024

@zweizeichen Hats off to you man...
But your caveat is not a result of the tweak done to overcome the NUXT SSR issue, is it?

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024

The caveat is caused by the behaviour of the merge function and will always occur when merging state using that method. See:

return function(store) {
const savedState = shvl.get(options, 'getState', getState)(key, storage);
if (typeof savedState === 'object' && savedState !== null) {
store.replaceState(merge(store.state, savedState, {
arrayMerge: function (store, saved) { return saved },
clone: false,
}));
}
(options.subscriber || subscriber)(store)(function(mutation, state) {
if ((options.filter || filter)(mutation)) {
(options.setState || setState)(
key,
(options.reducer || reducer)(state, options.paths || []),
storage
);
}
});
};
I hope this answers your question 😃

from vuex-persistedstate.

o1lab avatar o1lab commented on August 12, 2024

@ishitatsuyuki : that comment helped me to get vuex state stored in local storage with no problem.

However, then I installed vuex-router-sync - Now if I go to any /foo/bar url in webpage - application home page gets reloaded.

So close yet so far..

Anybody facing same issue ?

from vuex-persistedstate.

nikugogoi avatar nikugogoi commented on August 12, 2024

@zweizeichen thats what ... this caveat will arise even if we are using this plugin normally .... I mean, your solution has not caused this problem.... Am I wrong? this plugin is written in such a way

from vuex-persistedstate.

zweizeichen avatar zweizeichen commented on August 12, 2024

It's potentially unexpected behaviour which users of the workaround IMHO should be aware of. The behaviour is caused by the interaction between the workaround the the underlying code. Who exactly is at fault does not really matter as long we have software which behaves the way we expect it to :)

It may be 'fixed' by using another function for merging state, however that might break other use-cases. For my part I've stopped storing persistent local state for now (for entirely different reasons).

This issue seems to pop up pretty regularily since it was opened, this indicates there is a need in the Nuxt community for a proper solution for locally storing vuex state. Maybe someone wants to step forward and propose a solution either by PR or by adding a new package to the ever expanding JS ecosystem :)

from vuex-persistedstate.

nikugogoi avatar nikugogoi commented on August 12, 2024

@zweizeichen ok I seem to understand the problem now... Sorry for my repeated nagging, its just that I want to clear this up in my head before my shit hits production.... if I have a state which is an array

state : {
   testArray : [ foxtrot, alpha ],
   somethingElse : { key : 'value' }
}

then it is saved in browser storage(any storage), the problem arises if suppose the contents of testArray are changed to [ zulu, niner] (changed by something other than mutation?). testArray remains to be [ foxtrot, alpha ]
Does this example comply with the caveat? Or is it something else 😓 ?

from vuex-persistedstate.

robinvdvleuten avatar robinvdvleuten commented on August 12, 2024

It's in release v2.5.0. All the props for @zweizeichen for diving into this issue 👍

from vuex-persistedstate.

emaiax avatar emaiax commented on August 12, 2024

@syffs I'm still not being able to retrieve the state from localStorage without some horrible workarounds such as setInterval on mounted() hook.

Have you worked on anything lately?

from vuex-persistedstate.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.