Skip to content

Commit

Permalink
feat(nuxt): sync useCookie state between tabs (#20970)
Browse files Browse the repository at this point in the history
  • Loading branch information
xanderbarkhatov committed Jun 6, 2023
1 parent 7695aca commit a31899a
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 7 deletions.
4 changes: 0 additions & 4 deletions docs/3.api/1.composables/use-cookie.md
Expand Up @@ -18,10 +18,6 @@ const cookie = useCookie(name, options)
`useCookie` ref will automatically serialize and deserialize cookie value to JSON.
::

::alert{icon=⚠️}
Multiple invocations of `useCookie` with the same name are not synced. [You can utilise `useState()` to sync them as a workaround](https://github.com/nuxt/nuxt/issues/13020#issuecomment-1505548242).
::

## Example

The example below creates a cookie called `counter`. If the cookie doesn't exist, it is initially set to a random value. Whenever we update the `counter` variable, the cookie will be updated accordingly.
Expand Down
27 changes: 24 additions & 3 deletions packages/nuxt/src/app/composables/cookie.ts
@@ -1,5 +1,5 @@
import type { Ref } from 'vue'
import { ref, watch } from 'vue'
import { getCurrentInstance, nextTick, onUnmounted, ref, watch } from 'vue'
import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es'
import { parse, serialize } from 'cookie-es'
import { deleteCookie, getCookie, setCookie } from 'h3'
Expand Down Expand Up @@ -34,9 +34,30 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
const cookie = ref<T | undefined>(cookies[name] as any ?? opts.default?.())

if (process.client) {
const callback = () => { writeClientCookie(name, cookie.value, opts as CookieSerializeOptions) }
const channel = typeof BroadcastChannel === 'undefined' ? null : new BroadcastChannel(`nuxt:cookies:${name}`)
if (getCurrentInstance()) { onUnmounted(() => { channel?.close() }) }

const callback = () => {
writeClientCookie(name, cookie.value, opts as CookieSerializeOptions)
channel?.postMessage(cookie.value)
}

let watchPaused = false

if (channel) {
channel.onmessage = (event) => {
watchPaused = true
cookie.value = event.data
nextTick(() => { watchPaused = false })
}
}

if (opts.watch) {
watch(cookie, callback, { deep: opts.watch !== 'shallow' })
watch(cookie, (newVal, oldVal) => {
if (watchPaused || isEqual(newVal, oldVal)) { return }
callback()
},
{ deep: opts.watch !== 'shallow' })
} else {
callback()
}
Expand Down

0 comments on commit a31899a

Please sign in to comment.