Skip to content

Commit 9afeec6

Browse files
OrbisKantfu
andauthored
feat(useCached): add options.deepRefs (#4591)
Co-authored-by: Anthony Fu <github@antfu.me>
1 parent a7ad511 commit 9afeec6

File tree

8 files changed

+142
-7
lines changed

8 files changed

+142
-7
lines changed

packages/core/_configurable.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,12 @@ export const defaultWindow = /* #__PURE__ */ isClient ? window : undefined
3939
export const defaultDocument = /* #__PURE__ */ isClient ? window.document : undefined
4040
export const defaultNavigator = /* #__PURE__ */ isClient ? window.navigator : undefined
4141
export const defaultLocation = /* #__PURE__ */ isClient ? window.location : undefined
42+
43+
export interface ConfigurableDeepRefs<D extends boolean> {
44+
/**
45+
* Return deep refs instead of shallow refs.
46+
*
47+
* @default true - will be changed to `false` by default in the next major
48+
*/
49+
deepRefs?: D
50+
}

packages/core/useCached/index.test.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { describe, expect, it } from 'vitest'
2-
import { ref as deepRef, shallowRef } from 'vue'
1+
import { describe, expect, it, vi } from 'vitest'
2+
import { ref as deepRef, isShallow, shallowRef } from 'vue'
33
import { nextTwoTick } from '../../.test'
44
import { useCached } from './index'
55

@@ -58,4 +58,27 @@ describe('useCached', () => {
5858
expect(cachedArrayRef.value).not.toBe(initialArrayValue)
5959
expect(cachedArrayRef.value).toEqual([2])
6060
})
61+
62+
describe('should work with options.deepRefs', () => {
63+
// todo: change default with next major
64+
it.fails('should return shallowRef by default', () => {
65+
const value = deepRef(1)
66+
67+
const cachedValue = useCached(value, vi.fn())
68+
expect(isShallow(cachedValue)).toBe(true)
69+
})
70+
it('should return deepRef if true', () => {
71+
const value = deepRef(1)
72+
73+
const cachedValue = useCached(value, vi.fn(), { deepRefs: true })
74+
expect(isShallow(cachedValue)).toBe(false)
75+
})
76+
77+
it('should return shallowRef if false', () => {
78+
const value = deepRef(1)
79+
80+
const cachedValue = useCached(value, vi.fn(), { deepRefs: false })
81+
expect(isShallow(cachedValue)).toBe(true)
82+
})
83+
})
6184
})

packages/core/useCached/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
import type { ConfigurableDeepRefs } from '@vueuse/core/_configurable'
2+
import type { ShallowOrDeepRef } from '@vueuse/shared'
13
import type { Ref, WatchOptions } from 'vue'
2-
import { ref as deepRef, watch } from 'vue'
4+
import { createRef } from '@vueuse/shared'
5+
import { watch } from 'vue'
36

4-
export function useCached<T>(
7+
export interface UseCachedOptions<D extends boolean = true> extends ConfigurableDeepRefs<D>, WatchOptions {}
8+
9+
export function useCached<T, D extends boolean = true>(
510
refValue: Ref<T>,
611
comparator: (a: T, b: T) => boolean = (a, b) => a === b,
7-
watchOptions?: WatchOptions,
8-
): Ref<T> {
9-
const cachedValue = deepRef(refValue.value) as Ref<T>
12+
options?: UseCachedOptions<D>,
13+
): UseCachedReturn<T, D> {
14+
const { deepRefs = true as D, ...watchOptions } = options || {}
15+
const cachedValue = createRef(refValue.value, deepRefs)
1016

1117
watch(() => refValue.value, (value) => {
1218
if (!comparator(value, cachedValue.value))
@@ -15,3 +21,5 @@ export function useCached<T>(
1521

1622
return cachedValue
1723
}
24+
25+
export type UseCachedReturn<T = any, D extends boolean = true> = ShallowOrDeepRef<T, D>

packages/core/useSupported/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ export function useSupported(callback: () => unknown) {
1111
return Boolean(callback())
1212
})
1313
}
14+
15+
export type UseSupportedReturn = ReturnType<typeof useSupported>

packages/shared/createRef/index.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
category: Reactivity
3+
---
4+
5+
# createRef
6+
7+
Returns a `deepRef` or `shallowRef` depending on the `deep` param.
8+
9+
## Usage
10+
11+
```ts
12+
import { createRef } from '@vueuse/core'
13+
import { isShallow, ref } from 'vue'
14+
15+
const initialData = 1
16+
17+
const shallowData = createRef(initialData)
18+
const deepData = createRef(initialData, true)
19+
20+
isShallow(shallowData) // true
21+
isShallow(deepData) // false
22+
```
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { describe, expect, it, vi } from 'vitest'
2+
import { isShallow, nextTick, watch } from 'vue'
3+
import { createRef } from '.'
4+
5+
describe('createRef', () => {
6+
it('should return a ref', async () => {
7+
const a = createRef(1)
8+
expect(a.value).toBe(1)
9+
a.value = 5
10+
expect(a.value).toBe(5)
11+
const b = createRef({ foo: 'bar', baz: 42 })
12+
const bSpy = vi.fn()
13+
watch(b, bSpy, { deep: true })
14+
expect(b.value.foo).toBe('bar')
15+
expect(b.value.baz).toBe(42)
16+
b.value.foo = 'baz'
17+
b.value.baz = 43
18+
await nextTick()
19+
expect(bSpy).not.toHaveBeenCalled()
20+
const c = createRef({ foo: 'bar', baz: 42 }, true)
21+
const cSpy = vi.fn()
22+
watch(c, cSpy, { deep: true })
23+
expect(c.value.foo).toBe('bar')
24+
expect(c.value.baz).toBe(42)
25+
c.value.foo = 'baz'
26+
c.value.baz = 43
27+
await nextTick()
28+
expect(cSpy).toHaveBeenCalledOnce()
29+
})
30+
31+
describe('deep', () => {
32+
it('deep: default - returns shallow', () => {
33+
expect(isShallow(createRef(1))).toBe(true)
34+
})
35+
36+
it('deep: true - returns deep', () => {
37+
expect(isShallow(createRef(1, true))).toBe(false)
38+
})
39+
40+
it('deep: false - returns shallow', () => {
41+
expect(isShallow(createRef(1, false))).toBe(true)
42+
})
43+
})
44+
})

packages/shared/createRef/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { Ref, ShallowRef } from 'vue'
2+
import { ref as deepRef, shallowRef } from 'vue'
3+
4+
export type ShallowOrDeepRef<T = any, D extends boolean = false> = D extends true ? Ref<T> : ShallowRef<T>
5+
6+
/**
7+
* Returns a `deepRef` or `shallowRef` depending on the `deep` param.
8+
*
9+
* @example createRef(1) // ShallowRef<number>
10+
* @example createRef(1, false) // ShallowRef<number>
11+
* @example createRef(1, true) // Ref<number>
12+
* @example createRef("string") // ShallowRef<string>
13+
* @example createRef<"A"|"B">("A", true) // Ref<"A"|"B">
14+
*
15+
* @param value
16+
* @param deep
17+
* @returns the `deepRef` or `shallowRef`
18+
*/
19+
export function createRef<T = any, D extends boolean = false>(value: T, deep?: D): ShallowOrDeepRef<T, D> {
20+
if (deep === true) {
21+
return deepRef(value) as ShallowOrDeepRef<T, D>
22+
}
23+
else {
24+
return shallowRef(value) as ShallowOrDeepRef<T, D>
25+
}
26+
}

packages/shared/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './computedWithControl'
33
export * from './createEventHook'
44
export * from './createGlobalState'
55
export * from './createInjectionState'
6+
export * from './createRef'
67
export * from './createSharedComposable'
78
export * from './extendRef'
89
export * from './get'

0 commit comments

Comments
 (0)