mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 07:05:11 +00:00
fix(nuxt): watch custom cookieRef
values deeply (#26151)
This commit is contained in:
parent
5c284ffda8
commit
6407cea620
@ -55,7 +55,7 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
|
|||||||
|
|
||||||
// use a custom ref to expire the cookie on client side otherwise use basic ref
|
// use a custom ref to expire the cookie on client side otherwise use basic ref
|
||||||
const cookie = import.meta.client && delay && !hasExpired
|
const cookie = import.meta.client && delay && !hasExpired
|
||||||
? cookieRef<T | undefined>(cookieValue, delay)
|
? cookieRef<T | undefined>(cookieValue, delay, opts.watch && opts.watch !== 'shallow')
|
||||||
: ref<T | undefined>(cookieValue)
|
: ref<T | undefined>(cookieValue)
|
||||||
|
|
||||||
if (import.meta.dev && hasExpired) {
|
if (import.meta.dev && hasExpired) {
|
||||||
@ -123,7 +123,7 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
|
|||||||
return cookie as CookieRef<T>
|
return cookie as CookieRef<T>
|
||||||
}
|
}
|
||||||
/** @since 3.10.0 */
|
/** @since 3.10.0 */
|
||||||
export function refreshCookie(name: string) {
|
export function refreshCookie (name: string) {
|
||||||
if (store || typeof BroadcastChannel === 'undefined') return
|
if (store || typeof BroadcastChannel === 'undefined') return
|
||||||
|
|
||||||
new BroadcastChannel(`nuxt:cookies:${name}`)?.postMessage({ refresh: true })
|
new BroadcastChannel(`nuxt:cookies:${name}`)?.postMessage({ refresh: true })
|
||||||
@ -174,14 +174,21 @@ function writeServerCookie (event: H3Event, name: string, value: any, opts: Cook
|
|||||||
const MAX_TIMEOUT_DELAY = 2_147_483_647
|
const MAX_TIMEOUT_DELAY = 2_147_483_647
|
||||||
|
|
||||||
// custom ref that will update the value to undefined if the cookie expires
|
// custom ref that will update the value to undefined if the cookie expires
|
||||||
function cookieRef<T> (value: T | undefined, delay: number) {
|
function cookieRef<T> (value: T | undefined, delay: number, shouldWatch: boolean) {
|
||||||
let timeout: NodeJS.Timeout
|
let timeout: NodeJS.Timeout
|
||||||
|
let unsubscribe: (() => void) | undefined
|
||||||
let elapsed = 0
|
let elapsed = 0
|
||||||
|
const internalRef = shouldWatch ? ref(value) : { value }
|
||||||
if (getCurrentScope()) {
|
if (getCurrentScope()) {
|
||||||
onScopeDispose(() => { clearTimeout(timeout) })
|
onScopeDispose(() => {
|
||||||
|
unsubscribe?.()
|
||||||
|
clearTimeout(timeout)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return customRef((track, trigger) => {
|
return customRef((track, trigger) => {
|
||||||
|
if (shouldWatch) { unsubscribe = watch(internalRef, trigger) }
|
||||||
|
|
||||||
function createExpirationTimeout () {
|
function createExpirationTimeout () {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
const timeRemaining = delay - elapsed
|
const timeRemaining = delay - elapsed
|
||||||
@ -190,7 +197,7 @@ function cookieRef<T> (value: T | undefined, delay: number) {
|
|||||||
elapsed += timeoutLength
|
elapsed += timeoutLength
|
||||||
if (elapsed < delay) { return createExpirationTimeout() }
|
if (elapsed < delay) { return createExpirationTimeout() }
|
||||||
|
|
||||||
value = undefined
|
internalRef.value = undefined
|
||||||
trigger()
|
trigger()
|
||||||
}, timeoutLength)
|
}, timeoutLength)
|
||||||
}
|
}
|
||||||
@ -198,12 +205,12 @@ function cookieRef<T> (value: T | undefined, delay: number) {
|
|||||||
return {
|
return {
|
||||||
get () {
|
get () {
|
||||||
track()
|
track()
|
||||||
return value
|
return internalRef.value
|
||||||
},
|
},
|
||||||
set (newValue) {
|
set (newValue) {
|
||||||
createExpirationTimeout()
|
createExpirationTimeout()
|
||||||
|
|
||||||
value = newValue
|
internalRef.value = newValue
|
||||||
trigger()
|
trigger()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ describe('composables', () => {
|
|||||||
'useRequestFetch',
|
'useRequestFetch',
|
||||||
'isPrerendered',
|
'isPrerendered',
|
||||||
'useRequestHeaders',
|
'useRequestHeaders',
|
||||||
|
'useCookie',
|
||||||
'clearNuxtState',
|
'clearNuxtState',
|
||||||
'useState',
|
'useState',
|
||||||
'useRequestURL',
|
'useRequestURL',
|
||||||
@ -121,7 +122,6 @@ describe('composables', () => {
|
|||||||
'preloadRouteComponents',
|
'preloadRouteComponents',
|
||||||
'reloadNuxtApp',
|
'reloadNuxtApp',
|
||||||
'refreshCookie',
|
'refreshCookie',
|
||||||
'useCookie',
|
|
||||||
'useFetch',
|
'useFetch',
|
||||||
'useHead',
|
'useHead',
|
||||||
'useLazyFetch',
|
'useLazyFetch',
|
||||||
@ -628,6 +628,33 @@ describe('defineNuxtComponent', () => {
|
|||||||
it.todo('should support Options API head')
|
it.todo('should support Options API head')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('useCookie', () => {
|
||||||
|
it('should watch custom cookie refs', () => {
|
||||||
|
const user = useCookie('userInfo', {
|
||||||
|
default: () => ({ score: -1 }),
|
||||||
|
maxAge: 60 * 60,
|
||||||
|
})
|
||||||
|
const computedVal = computed(() => user.value.score)
|
||||||
|
expect(computedVal.value).toBe(-1)
|
||||||
|
user.value.score++
|
||||||
|
expect(computedVal.value).toBe(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not watch custom cookie refs when shallow', () => {
|
||||||
|
for (const value of ['shallow', false] as const) {
|
||||||
|
const user = useCookie('shallowUserInfo', {
|
||||||
|
default: () => ({ score: -1 }),
|
||||||
|
maxAge: 60 * 60,
|
||||||
|
watch: value
|
||||||
|
})
|
||||||
|
const computedVal = computed(() => user.value.score)
|
||||||
|
expect(computedVal.value).toBe(-1)
|
||||||
|
user.value.score++
|
||||||
|
expect(computedVal.value).toBe(-1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('callOnce', () => {
|
describe('callOnce', () => {
|
||||||
it('should only call composable once', async () => {
|
it('should only call composable once', async () => {
|
||||||
const fn = vi.fn()
|
const fn = vi.fn()
|
||||||
@ -643,7 +670,7 @@ describe('callOnce', () => {
|
|||||||
await Promise.all([execute(), execute(), execute()])
|
await Promise.all([execute(), execute(), execute()])
|
||||||
expect(fn).toHaveBeenCalledTimes(1)
|
expect(fn).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
const fnSync = vi.fn().mockImplementation(() => { })
|
const fnSync = vi.fn().mockImplementation(() => {})
|
||||||
const executeSync = () => callOnce(fnSync)
|
const executeSync = () => callOnce(fnSync)
|
||||||
await Promise.all([executeSync(), executeSync(), executeSync()])
|
await Promise.all([executeSync(), executeSync(), executeSync()])
|
||||||
expect(fnSync).toHaveBeenCalledTimes(1)
|
expect(fnSync).toHaveBeenCalledTimes(1)
|
||||||
|
Loading…
Reference in New Issue
Block a user