fix(nuxt): watch custom cookieRef values deeply (#26151)

This commit is contained in:
Daniel Roe 2024-03-08 17:03:31 +00:00 committed by GitHub
parent 5c284ffda8
commit 6407cea620
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 10 deletions

View File

@ -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
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)
if (import.meta.dev && hasExpired) {
@ -123,7 +123,7 @@ export function useCookie<T = string | null | undefined> (name: string, _opts?:
return cookie as CookieRef<T>
}
/** @since 3.10.0 */
export function refreshCookie(name: string) {
export function refreshCookie (name: string) {
if (store || typeof BroadcastChannel === 'undefined') return
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
// 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 unsubscribe: (() => void) | undefined
let elapsed = 0
const internalRef = shouldWatch ? ref(value) : { value }
if (getCurrentScope()) {
onScopeDispose(() => { clearTimeout(timeout) })
onScopeDispose(() => {
unsubscribe?.()
clearTimeout(timeout)
})
}
return customRef((track, trigger) => {
if (shouldWatch) { unsubscribe = watch(internalRef, trigger) }
function createExpirationTimeout () {
clearTimeout(timeout)
const timeRemaining = delay - elapsed
@ -190,7 +197,7 @@ function cookieRef<T> (value: T | undefined, delay: number) {
elapsed += timeoutLength
if (elapsed < delay) { return createExpirationTimeout() }
value = undefined
internalRef.value = undefined
trigger()
}, timeoutLength)
}
@ -198,12 +205,12 @@ function cookieRef<T> (value: T | undefined, delay: number) {
return {
get () {
track()
return value
return internalRef.value
},
set (newValue) {
createExpirationTimeout()
value = newValue
internalRef.value = newValue
trigger()
}
}

View File

@ -97,6 +97,7 @@ describe('composables', () => {
'useRequestFetch',
'isPrerendered',
'useRequestHeaders',
'useCookie',
'clearNuxtState',
'useState',
'useRequestURL',
@ -121,7 +122,6 @@ describe('composables', () => {
'preloadRouteComponents',
'reloadNuxtApp',
'refreshCookie',
'useCookie',
'useFetch',
'useHead',
'useLazyFetch',
@ -628,6 +628,33 @@ describe('defineNuxtComponent', () => {
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', () => {
it('should only call composable once', async () => {
const fn = vi.fn()
@ -643,7 +670,7 @@ describe('callOnce', () => {
await Promise.all([execute(), execute(), execute()])
expect(fn).toHaveBeenCalledTimes(1)
const fnSync = vi.fn().mockImplementation(() => { })
const fnSync = vi.fn().mockImplementation(() => {})
const executeSync = () => callOnce(fnSync)
await Promise.all([executeSync(), executeSync(), executeSync()])
expect(fnSync).toHaveBeenCalledTimes(1)