gabrielmbmb / vuex-multi-tab-state

πŸ’ΎπŸ”—πŸ–₯️ Share, synchronize and persist state between multiple tabs with this plugin for Vuex. TypeScript types included.
MIT License
160 stars 17 forks source link

vuex-multi-tab-state and vuex-persistedstate #63

Open AndreiSoroka opened 3 years ago

AndreiSoroka commented 3 years ago

Hi. I simultaneously usage vuex-multi-tab-state and vuex-persistedstate. Both usage localstorage.

Does vuex-multi-tab-state completely replace vuex-persistedstate functionality?

thanks

wparad commented 3 years ago

It's worse than duplicate storage, there is no way to know when the user first opens the site. Arguably the functionality should be:

When the last tab is closed, vuex-multi-tab-state wipes it cache.

From a practical standpoint this would actually mean, setting some sort of initialization flag to populate the state from the vuex store and then using it from that point on, until the vuex store resets itself. Perhaps it is as simple as setting a flag in vuex (making sure it is never persisted) and then checking the flag before loading/overwriting the state. (and then setting the flag)

AndreiSoroka commented 3 years ago

Temporrary, I always remove from localstorage when user is open new tab

window.localStorage.removeItem('vuex-multi-tab');

Each tab register two modules (vuex-multi-tab-state, vuex-persistedstate)

And it is works. (First tab work with recreated vuex-multi-tab)

But I think it is temporrary solve... I'm afraid this "bike" will break...

wparad commented 3 years ago

That's a great first start, but I think you'll also need to handle resuming a sleeping tab as well.

gabrielmbmb commented 3 years ago

Hey, thank you for submitting this issue. I'll put a boolean flag in the plugin initialization indicating whether or not the content in the local storage should be removed after the last tab has been closed. What do you think?

wparad commented 3 years ago

Its the right direction, but the problem is there is no reliable way to know when a tab is closed. For instance if the browser is closed itself or all tabs are put into the background, it would need to be treated the same but no tabs have actually been closed. Maybe more realistic would be an initialize/enable method, or even better an injected computed property that causes the plugin to clone the current store, wipe its cache and start syncing the in memory store.

I think it has to continually check "if enabled" as that state can change even if the tab isn't closed. It's probably sufficient with a method, as long as the value isn't persisted. Though I don't see a straightforward way to do that. So the computed property reactivity check is probably the only way.

AndreiSoroka commented 3 years ago

Yes. Need research first initialization.

How I see:

If I inited plugin with argument: {autoClearCache: true} then:

  1. Open new tab
  2. Remove cache from localstorage
  3. Init vuex
  4. Add to localstorage

If I have already opened tab:

  1. Listen localstorage (If somebody remove and add localstorage, tab is getting event)
  2. If somebody change vuex and localstorage is empty > just create
KaskoYurii commented 1 year ago

I have the same issue: New changes in the store do not appear until I have not reset the all browser cache. If I will clear localStorage on the tab opening, I'm not sure that this solution will work while I just reload my tab. Maybe there is an updated solution?

cepm-nate commented 3 months ago

I think we need a new solution wherein the Vuex store is actually entirely in a shared worker, and then every tab that opens creates a mirrored store which sends all it's actions, mutations, and getters to the shared worker store, and the shared worker store does all the real work, then emits an state changes out to the mirrored stores. I use "mirrored" here, but in reality they are more like pass-through stores. That way the 'persisted' plugin would run on the shared worker store, and it wouldn't matter when you opened a tab or how many...etc. I've started working on such a setup, but would be very happy to see someone else officially create such a thing. Anyone know of someone else who's tried it?

wparad commented 3 months ago

I think we need a new solution wherein the Vuex store is actually entirely in a shared worker, and then every tab that opens creates a mirrored store which sends all it's actions, mutations, and getters to the shared worker store, and the shared worker store does all the real work, then emits an state changes out to the mirrored stores. I use "mirrored" here, but in reality they are more like pass-through stores. That way the 'persisted' plugin would run on the shared worker store, and it wouldn't matter when you opened a tab or how many...etc. I've started working on such a setup, but would be very happy to see someone else officially create such a thing. Anyone know of someone else who's tried it?

That's exactly the right solution to have. It could be challenging figuring out the polling window, I wonder if there are any browser apis for this, or if some sort service worker is the recommended solution. It wouldn't be great constantly be grabbing all the data from localstore every millisecond, I can't even imagine this is a Vue problem. Literally every SPA and MPA has this issue.

cepm-nate commented 2 months ago

I've implemented a solution which works, but is a little bit messy.

It's messy for the following reasons:

That's why an OFFICIAL SharedStorage plugin would be neat. Especially if it integrated with building tools, and handled all the communications automatically.

cepm-nate commented 2 months ago

I've refined my approach to be the following:

Biggest downside of this entire setup is having to swap all store.state references in the main app (per tab) to be asyncComputed getters. This makes the app a little bit slower, as it now does an initial render, then re-renders the DOM as each asyncComputed property resolves. Prior to this swapout, all the computer properties directly depending on store.state values would be calculated at once, then the DOM would be generated.

If anyone has ideas on how to make my approach work better or would like to see snippets of code, let me know!