Skip to content

Commit

Permalink
feat(useCached): new function (vitest-dev#1169)
Browse files Browse the repository at this point in the history
Co-authored-by: wangwenlu <wangwenlu@kuaishou.com>
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
  • Loading branch information
3 people committed Feb 8, 2022
1 parent b9a2a1d commit fb37135
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 0 deletions.
1 change: 1 addition & 0 deletions packages/.test/index.ts
@@ -1,3 +1,4 @@
export * from './mount'
export * from './polyfillFetch'
export * from './retry'
export * from './nextTick'
5 changes: 5 additions & 0 deletions packages/.test/nextTick.ts
@@ -0,0 +1,5 @@
export const nextTwoTick = () => new Promise<void>((resolve) => {
setTimeout(() => {
setTimeout(resolve)
})
})
1 change: 1 addition & 0 deletions packages/core/index.ts
Expand Up @@ -15,6 +15,7 @@ export * from './useBattery'
export * from './useBreakpoints'
export * from './useBroadcastChannel'
export * from './useBrowserLocation'
export * from './useCached'
export * from './useClamp'
export * from './useClipboard'
export * from './useColorMode'
Expand Down
48 changes: 48 additions & 0 deletions packages/core/useCached/demo.vue
@@ -0,0 +1,48 @@
<script setup lang="ts">
import { ref } from 'vue-demi'
import { useCached } from '.'
interface Value {
value: number
extra: number
}
const value = ref<Value>({ value: 42, extra: 0 })
const comparator = (a: Value, b: Value) => a.value === b.value
const cachedValue = useCached(value, comparator)
const inputValue = ref(value.value.value)
const inputExtra = ref(value.value.extra)
const onSyncClick = () => {
value.value = {
value: inputValue.value,
extra: inputExtra.value,
}
}
</script>

<template>
<div>
<div>
<div>Value: {{ value.value }}</div>
<div>Extra: {{ value.extra }}</div>
<div>Cached Value: {{ cachedValue.value }}</div>
<div>Cached Extra: {{ cachedValue.extra }}</div>

<div>
<label for="localValue">Temp Value: </label>
<input id="localValue" v-model.number="inputValue">
</div>
<div>
<label for="localExtra">Local Extra: </label>
<input id="localExtra" v-model.number="inputExtra">
</div>
<div>
<button @click="onSyncClick">
Sync
</button>
</div>
</div>
</div>
</template>
35 changes: 35 additions & 0 deletions packages/core/useCached/index.md
@@ -0,0 +1,35 @@
---
category: Utilities
---

# useCached

Cache a ref with a custom comparator.

## Usage

```ts
import { useCached } from '@vueuse/core'

interface Data {
value: number
extra: number
}

const source = ref<Data>({ value: 42, extra: 0 })
const cached = useCached(value, (a, b) => a.value === b.value)

source.value = {
value: 42,
extra: 1
}

console.log(cached.value) // { value: 42, extra: 0 }

source.value = {
value: 43,
extra: 1
}

console.log(cached.value) // { value: 43, extra: 1 }
```
46 changes: 46 additions & 0 deletions packages/core/useCached/index.test.ts
@@ -0,0 +1,46 @@
import { ref } from 'vue-demi'
import { nextTwoTick } from '../../.test'
import { useCached } from '.'

function arrayEquals<T>(a: T[], b: T[]): boolean {
if (a.length !== b.length)
return false

for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i])
return false
}
return true
}

describe('useCached', () => {
it('should be defined', () => {
expect(useCached).toBeDefined()
})

it('should work', async() => {
const arrayRef = ref([1])
const initialArrayValue = arrayRef.value

const cachedArrayRef = useCached(arrayRef, arrayEquals)
await nextTwoTick()

expect(cachedArrayRef.value).toBe(initialArrayValue)

arrayRef.value = initialArrayValue
await nextTwoTick()

expect(cachedArrayRef.value).toBe(initialArrayValue)

arrayRef.value = [1]
await nextTwoTick()

expect(cachedArrayRef.value).toBe(initialArrayValue)

arrayRef.value = [2]
await nextTwoTick()

expect(cachedArrayRef.value).not.toBe(initialArrayValue)
expect(cachedArrayRef.value).toEqual([2])
})
})
17 changes: 17 additions & 0 deletions packages/core/useCached/index.ts
@@ -0,0 +1,17 @@
import type { Ref, WatchOptions } from 'vue-demi'
import { ref, watch } from 'vue-demi'

export function useCached<T>(
refValue: Ref<T>,
comparator: (a: T, b: T) => boolean = (a, b) => a === b,
watchOptions?: WatchOptions,
): Ref<T> {
const cachedValue = ref(refValue.value) as Ref<T>

watch(() => refValue.value, (value) => {
if (!comparator(value, cachedValue.value))
cachedValue.value = value
}, watchOptions)

return cachedValue
}

0 comments on commit fb37135

Please sign in to comment.