Skip to content

Commit

Permalink
feat(utils): add debouncedRef function
Browse files Browse the repository at this point in the history
- create a reference to the original value from the Vue instance, and only update the original after debouncing
  • Loading branch information
pdanpdan committed May 5, 2023
1 parent 7fe8b41 commit 9bd33c7
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 0 deletions.
27 changes: 27 additions & 0 deletions docs/src/pages/quasar-utils/other-utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,33 @@ runSequentialPromises([ /* ... */ ], { threadsNumber: 3 })
})
```

## Debounced Ref <q-badge align="top" label="v1.14+" />
Creates a new reference to an existing value in the Vue instance that will be updated in real-time, while the original value will be updated after a debouncing period.

The new value can be used as model for a control that updates frequently (eg. QInput, QSlider, QRange, ...), while the original value will only be updated after debouncing (eg. You have a search field but you only want to perform the remote search API call 300ms after the last change in the search field).

``` js
import { debouncedRef } from 'quasar'

....
data () {
return {
search: '',
debouncedSearch: debouncedRef(this, 'search', 500)
}
},

watch: {
debouncedSearch (newSearch, oldSearch) {
...
}
}
```

::: warning
If the debounced value is an object only modify it as a whole, because it is passed by reference. If you change the value of keys inside the object the change will propagate immediatelly.
:::

## Debounce Function
If your App uses JavaScript to accomplish taxing tasks, a debounce function is essential to ensuring a given task doesn't fire so often that it bricks browser performance. Debouncing a function limits the rate at which the function can fire.

Expand Down
72 changes: 72 additions & 0 deletions ui/dev/src/pages/form/debounced-ref.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<template>
<div class="q-layout-padding column q-gutter-y-md">
<div class="q-pa-md bg-grey-3">
<div>original1: [{{ original1 }}]</div>
<input type="number" v-model.number="original1" step="1" />

<div>debounced1: [{{ debounced1.value }}]</div>
<input type="number" v-model.number="debounced1.value" step="1" />
<q-input type="number" v-model.number="debounced1.value" step="1" />
<q-slider v-model="debounced1.value" :step="1" />
</div>

<div class="q-pa-md bg-grey-3">
<div>original2: [{{ original2 }}]</div>
<input type="number" v-model.number="original2.test" step="1" />

<div>debounced2test: [{{ debounced2test.value }}]</div>
<input type="number" v-model.number="debounced2test.value" step="1" />
<q-input type="number" v-model.number="debounced2test.value" step="1" />
<q-slider v-model="debounced2test.value" :step="1" />
</div>

<div class="q-pa-md bg-grey-3">
<div>original3: [{{ original3 }}]</div>
<input type="number" v-model.number="original3.min" step="1" />
<input type="number" v-model.number="original3.max" step="1" />

<div>debounced3: [{{ debounced3.value }}]</div>
<q-range v-model="debounced3.value" drag-range :step="1" />
</div>
</div>
</template>

<script>
import { debouncedRef } from 'quasar'
export default {
data () {
return {
original1: 1,
debounced1: debouncedRef(this, 'original1', 1000),
original2: {
test: 2,
other: 3
},
debounced2test: debouncedRef(this, 'original2test', 1000),
original3: {
min: 10,
max: 40
},
debounced3: debouncedRef(this, 'original3', 1000)
}
},
computed: {
original2test: {
get () { return this.original2.test },
set (value) { this.original2.test = value }
}
},
beforeDestroy () {
console.log('beforeDestroy 1', this.original1, this.original2.test, this.original3.min, this.original3.max)
this.$nextTick(() => {
console.log('beforeDestroy 2', this.original1, this.original2.test, this.original3.min, this.original3.max)
})
}
}
</script>
2 changes: 2 additions & 0 deletions ui/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import colors from './utils/colors.js'
import copyToClipboard from './utils/copy-to-clipboard.js'
import date from './utils/date.js'
import debounce from './utils/debounce.js'
import debouncedRef from './utils/debounced-ref.js'
import dom from './utils/dom.js'
import event, { noop } from './utils/event.js'
import exportFile from './utils/export-file.js'
Expand All @@ -24,6 +25,7 @@ export {
copyToClipboard,
date,
debounce,
debouncedRef,
dom,
event,
noop,
Expand Down
48 changes: 48 additions & 0 deletions ui/src/utils/debounced-ref.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export default function (vm, prop, wait = 250) {
const debounced = {
get value () {
const value = vm[prop]

if (value !== debounced.propValue) {
debounced.propValue = value
debounced.internalValue = value

debounced.timer !== void 0 && clearTimeout(debounced.timer)
debounced.timer = void 0
}

return debounced.internalValue
},

set value (value) {
if (debounced.internalValue === value) {
return
}

debounced.internalValue = value

debounced.timer !== void 0 && clearTimeout(debounced.timer)
debounced.timer = setTimeout(() => {
vm[prop] = value

debounced.timer = void 0
}, wait)
},

destroy () {
if (debounced.timer !== void 0) {
clearTimeout(debounced.timer)
debounced.timer = void 0

vm[prop] = debounced.internalValue
}
}
}

vm.$once('hook:beforeDestroy', debounced.destroy)

debounced.propValue = NaN
debounced.internalValue = NaN

return debounced
}
6 changes: 6 additions & 0 deletions ui/types/utils.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LooseDictionary } from './ts-helpers'

export * from './utils/date';
export * from './utils/colors';
export * from './utils/dom';
Expand All @@ -14,6 +16,10 @@ export function debounce<F extends (...args: any[]) => any>(
wait?: number,
immediate?: boolean
): F & { cancel(): void };
export function debouncedRef<T extends LooseDictionary, K extends keyof T>(vm: T, prop: K, wait?: number): {
value: T[K],
destroy: () => void
};
export function exportFile(
fileName: string,
rawData: BlobPart,
Expand Down

0 comments on commit 9bd33c7

Please sign in to comment.