Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(useRouteQuery,useRouteParams): prevent reset on other scope dispose #3418

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 31 additions & 0 deletions packages/router/useRouteParams/index.test.ts
Expand Up @@ -144,6 +144,37 @@ describe('useRouteParams', () => {
expect(lang.value).toBeNull()
})

it('should not reset params to default if is disposed from other scope', async () => {
let route = getRoute()

const router = { replace: (r: any) => route = r } as any

const scopeA = effectScope()
const scopeB = effectScope()

route.params.page = 2

const defaultPage = 'DEFAULT_PAGE'
let page1: Ref<any> = ref(null)
await scopeA.run(async () => {
page1 = useRouteParams('page', defaultPage, { route, router })
})

let page2: Ref<any> = ref(null)
await scopeB.run(async () => {
page2 = useRouteParams('page', defaultPage, { route, router })
})

expect(page1.value).toBe(2)
expect(page2.value).toBe(2)

scopeA.stop()
await nextTick()

expect(page1.value).toBe(defaultPage)
expect(page2.value).toBe(2)
})

it('should change the value when the route changes', () => {
let route = getRoute()
const router = { replace: (r: any) => route = r } as any
Expand Down
36 changes: 21 additions & 15 deletions packages/router/useRouteParams/index.ts
Expand Up @@ -3,10 +3,10 @@ import { useRoute, useRouter } from 'vue-router'
import { toValue, tryOnScopeDispose } from '@vueuse/shared'
import type { Ref } from 'vue-demi'
import type { MaybeRefOrGetter } from '@vueuse/shared'
import type { LocationAsRelativeRaw, RouteParamValueRaw } from 'vue-router'
import type { LocationAsRelativeRaw, RouteParamValueRaw, Router } from 'vue-router'
import type { ReactiveRouteOptionsWithTransform } from '../_types'

const _cache = new WeakMap()
const _queue = new WeakMap<Router, Map<string, any>>()

export function useRouteParams(
name: string
Expand Down Expand Up @@ -36,17 +36,17 @@ export function useRouteParams<
transform = value => value as any as K,
} = options

if (!_cache.has(route))
_cache.set(route, new Map())
if (!_queue.has(router))
_queue.set(router, new Map())

const _params: Map<string, any> = _cache.get(route)
const _paramsQueue = _queue.get(router)!

let param = route.params[name] as any

tryOnScopeDispose(() => {
_params.delete(name)
param = undefined
})

_params.set(name, route.params[name])

let _trigger: () => void

const proxy = customRef<any>((track, trigger) => {
Expand All @@ -56,24 +56,30 @@ export function useRouteParams<
get() {
track()

const data = _params.get(name)

return transform(data !== undefined ? data : toValue(defaultValue))
return transform(param !== undefined ? param : toValue(defaultValue))
},
set(v) {
if (_params.get(name) === v)
if (param === v)
return

_params.set(name, v)
param = v
_paramsQueue.set(name, v)

trigger()

nextTick(() => {
if (_paramsQueue.size === 0)
return

const newParams = Object.fromEntries(_paramsQueue.entries())
_paramsQueue.clear()

const { params, query, hash } = route

router[toValue(mode)]({
params: {
...params,
...Object.fromEntries(_params.entries()),
...newParams,
},
query,
hash,
Expand All @@ -86,7 +92,7 @@ export function useRouteParams<
watch(
() => route.params[name],
(v) => {
_params.set(name, v)
param = v

_trigger()
},
Expand Down
32 changes: 31 additions & 1 deletion packages/router/useRouteQuery/index.test.ts
Expand Up @@ -142,6 +142,36 @@ describe('useRouteQuery', () => {
expect(lang.value).toBeNull()
})

it('should not reset params to default params is disposed from other scope', async () => {
let route = getRoute()

const router = { replace: (r: any) => route = r } as any
const scopeA = effectScope()
const scopeB = effectScope()

route.query.page = 2

const defaultPage = 'DEFAULT_PAGE'
let page1: Ref<any> = ref(null)
await scopeA.run(async () => {
page1 = useRouteQuery('page', defaultPage, { route, router })
})

let page2: Ref<any> = ref(null)
await scopeB.run(async () => {
page2 = useRouteQuery('page', defaultPage, { route, router })
})

expect(page1.value).toBe(2)
expect(page2.value).toBe(2)

scopeA.stop()
await nextTick()

expect(page1.value).toBe(defaultPage)
expect(page2.value).toBe(2)
})

it('should change the value when the route changes', () => {
let route = getRoute()
const router = { replace: (r: any) => route = r } as any
Expand All @@ -155,7 +185,7 @@ describe('useRouteQuery', () => {
expect(page.value).toBe('2')
})

it ('should differentiate null and undefined', () => {
it('should differentiate null and undefined', () => {
let route = getRoute({
page: 1,
})
Expand Down
34 changes: 20 additions & 14 deletions packages/router/useRouteQuery/index.ts
@@ -1,11 +1,12 @@
import { customRef, nextTick, watch } from 'vue-demi'
import { toValue, tryOnScopeDispose } from '@vueuse/shared'
import { useRoute, useRouter } from 'vue-router'
import type { Router } from 'vue-router'
import type { Ref } from 'vue-demi'
import type { MaybeRefOrGetter } from '@vueuse/shared'
import type { ReactiveRouteOptionsWithTransform, RouteQueryValueRaw } from '../_types'

const _cache = new WeakMap()
const _queue = new WeakMap<Router, Map<string, any>>()

export function useRouteQuery(
name: string
Expand Down Expand Up @@ -35,17 +36,17 @@ export function useRouteQuery<
transform = value => value as any as K,
} = options

if (!_cache.has(route))
_cache.set(route, new Map())
if (!_queue.has(router))
_queue.set(router, new Map())

const _query: Map<string, any> = _cache.get(route)
const _queriesQueue = _queue.get(router)!

let query = route.query[name] as any

tryOnScopeDispose(() => {
_query.delete(name)
query = undefined
})

_query.set(name, route.query[name])

let _trigger: () => void

const proxy = customRef<any>((track, trigger) => {
Expand All @@ -55,24 +56,29 @@ export function useRouteQuery<
get() {
track()

const data = _query.get(name)

return transform(data !== undefined ? data : toValue(defaultValue))
return transform(query !== undefined ? query : toValue(defaultValue))
},
set(v) {
if (_query.get(name) === v)
if (query === v)
return

_query.set(name, v)
query = v
_queriesQueue.set(name, v)

trigger()

nextTick(() => {
if (_queriesQueue.size === 0)
return

const newQueries = Object.fromEntries(_queriesQueue.entries())
_queriesQueue.clear()

const { params, query, hash } = route

router[toValue(mode)]({
params,
query: { ...query, ...Object.fromEntries(_query.entries()) },
query: { ...query, ...newQueries },
hash,
})
})
Expand All @@ -83,7 +89,7 @@ export function useRouteQuery<
watch(
() => route.query[name],
(v) => {
_query.set(name, v)
query = v

_trigger()
},
Expand Down