vueuse
vueuse copied to clipboard
useStorage reactivity between components not working
Describe the bug
If two components share the same local storage they do not respond reactive if one of them changes the value of the binding. After a page reload they are in sync. But if the value is changed again they are not reactive as before.
Reproduction
https://play.vueuse.org/#N4IgDghgxg1hDmBTAziAXAbVAOwgW0XRADcBXRAWgBNE8BLEAGhGQHtSAnKQtEU7MDHhMQNZFA50wAFzqtsRAGrkABABFadFQAoAFhAA2AMxV1sKgGIdE2KLoCUp5CogqaxRAdZgz8FaVkDOmkATxcDLwB3ZxD2FWlWFUjJaUQVAFVsOg8OZEMVZTSAGToAIw4ISRQVI1YOAtUAJhUAMhUAZhFOAyJdaWkwZDQAemH+QXgAOihWPGGyShp6YaDS4bMaAA9JvAArVABfRhx8HhAAAQXSZERh5H1rKhE2Tm4icaERMQkpWXkiADKD0QVAaiHSN38gWCdBQky6HB6vD6AyGow+Uxmc0u5Gut3ulRB5wArJMAAyTACM62wW0mKDwk32ICOJwIRBxiDxwxm1me7C4ZwxXxQPxkcgUvAAwqwIogoH9zKwTCgbthZPlCioZXgwKxkDD5BlobIUAikSAUYMRmMBEJprNhpzubzECTyVSaXSGUzDgBdZhGOgGM1oUAAQTAYEmC3QoFSuoMEFSRAAPFRsgAdcwqeCSKgAXkzIAAfjUohQZgZcxAwBQACzF7MqFRgJPcIsgGbqmzSSu9xD1YK0ZD9nscJs53Sd8TWGyTlu1dWdvDyVgLlQAPmbKlTZjAAR3LZbUCTyGQndqHDwld08pgFH3h5AR+PoTAiE7dnvpVYmw3x4qBwsqfsWyCRMEdgAcedCFsWRghpsAIQdIdhSnesAaEYECkAY0jQS2xAUKuNAGJerDri+OYtsM245pGYBoCowDALUrAHAcO6pgAQpUKi0dmqbDBmxCbs8YrSEQdC6nU0jMf4NwAgkFRICoBw1MBeAqMWzo3DydSIMWADc2bSXqHBybx9RGJpKgAOSTMMVkxuQdnZu5tjyMgclsSoBYKYgSl1AgiDaPBFHFowNSGDc9hGfxwxAYg0icNgzgAEqIEYqa-iBEDYHRLLMM5sZhiACZtsmZzptkdEtlZTEsWxHGCcJtXiZIMhSTJFnyXiQUqWk6k2bM2kXFcemusZHndt5NQUX5AUDSFYUgGxkXRQYsXxaMSUpRwaUqJl2W5SG+WFRxBxAA
System Info
System:
OS: macOS 12.3.1
CPU: (8) arm64 Apple M1
Memory: 143.25 MB / 16.00 GB
Shell: 5.8 - /bin/zsh
Binaries:
Node: 16.13.0 - /usr/local/bin/node
Yarn: 1.22.17 - ~/.yarn/bin/yarn
npm: 8.1.0 - /usr/local/bin/npm
Browsers:
Brave Browser: 101.1.38.109
Chrome: 101.0.4951.54
Firefox: 98.0.2
Safari: 15.4
npmPackages:
@vueuse/core: ^8.4.2 => 8.4.2
vue: ^3.2.33 => 3.2.33
Used Package Manager
yarn
Validations
- [X] Follow our Code of Conduct
- [X] Read the Contributing Guidelines.
- [X] Read the docs.
- [X] Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
- [X] Make sure this is a VueUse issue and not a framework-specific issue. For example, if it's a Vue SFC related bug, it should likely be reported to https://github.com/vuejs/core instead.
- [X] Check that this is a concrete bug. For Q&A open a GitHub Discussion.
- [X] The provided reproduction is a minimal reproducible example of the bug.
I think this makes sense. You are essentially initialising two refs in two different components. They are both retrieving their initial value from storage, but they are then independent of each other.
If we unpack useStorage, what you essentially have here is:
const storageKey = 'key'
const initialValue = localStorage.getItem(storageKey)
const foo = ref(initialValue)
const bar = ref(initialValue)
watch(foo, foo => localStorage.setItem(storageKey, foo)
watch(bar, bar => localStorage.setItem(storageKey, bar)
When foo changes in the above, it will update the locally stored value.
However, bar does not have any kind of watcher on localStorage. It won't be updated until the component is initialised again, and initialValue is retrieved again.
In your use case, you need to actually use the same ref in both components. Do this in the usual Vue way by passing it as a prop, injecting it, or exporting the ref from a composable .js/.ts file.
Thanks for your reply. Regarding the watcher: If I remove the value from localStorage it gets immediately updated, so isn't there a watcher?
Also, if you open this link in two separate windows the changes are visible immediately with localStorage acting as a single source of truth: https://vueuse.org/core/usestorage/#demo
This is why I do not understand that it does not work in my case.
Also, if you open this link in two separate windows the changes are visible immediately with localStorage acting as a single source of truth
Yes, that's really confusing. See the demo below, it will sync when update the value in different windows, but won't get synced within the same one.

Seems to be the limitation from storage event.
I had the same problem. If use prop, the flexibility is reduced
any way around this?
Thanks so much for fixing this, @antfu! Now I can delete around a dozen provide/inject calls. 🎉
Hello, the @antfu patch no longer seems to be working 🤔
Example with Nuxt 3.8.2 and VueUse 10.7.0
./app.vue
<template>
<h1>App</h1>
</template>
<script setup lang="ts">
import { StorageSerializers } from '@vueuse/core'
import type { UseStorageOptions } from '@vueuse/core'
const localStorageOpts: UseStorageOptions<any> = {
serializer: StorageSerializers.object
}
const localValue = useLocalStorage<object|null>('localValue', null, localStorageOpts)
onBeforeMount(() => {
console.log('app')
localValue.value = {
prop1: 'value1',
prop2: 'value2'
}
console.log('localValue', localValue.value) // ok (object)
})
</script>
./pages/folder/index.vue
<template>
<h1>Page</h1>
</template>
<script setup lang="ts">
import { StorageSerializers } from '@vueuse/core'
import type { UseStorageOptions } from '@vueuse/core'
const localStorageOpts: UseStorageOptions<any> = {
serializer: StorageSerializers.object
}
const localValue = useLocalStorage<object|null>('localValue', null, localStorageOpts)
onBeforeMount(() => {
console.log('page')
console.log('localValue', localValue.value) // not ok (null instead object)
})
</script>
Dev console:
- app
- localValue { prop1: 'value1', prop2: 'value2' }
- page
- localValue null