Skip to content

Commit e40ea53

Browse files
jy0529pikax
andauthored
feat(useTimeout): add a new composable (#740)
* feat(composable): add a new composable * chore: update tests to use fake timers Co-authored-by: pikax <carlos@hypermob.co.uk>
1 parent fe5615e commit e40ea53

File tree

5 files changed

+244
-0
lines changed

5 files changed

+244
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<template>
2+
<div>
3+
<p />
4+
<button @click="show">Show an alert box after two seconds</button>
5+
<p />
6+
<button @click="cancel">Cancel alert before it happens</button>
7+
</div>
8+
</template>
9+
<script>
10+
import { useTimeout } from "vue-composable";
11+
import { defineComponent, reactive } from "@vue/composition-api";
12+
13+
export default defineComponent({
14+
name: "timeout-example",
15+
setup() {
16+
let cancelTimeout;
17+
18+
function show() {
19+
const { cancel } = useTimeout(() => {
20+
alert("useTimeout callback invoked");
21+
}, 2000);
22+
cancelTimeout = cancel;
23+
}
24+
25+
function cancel() {
26+
cancelTimeout();
27+
}
28+
29+
return {
30+
show,
31+
cancel,
32+
};
33+
},
34+
});
35+
</script>

docs/composable/web/timeout.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Timeout
2+
3+
> The [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout).
4+
5+
## Parameters
6+
7+
```js
8+
import { useTimeout } from "vue-composable";
9+
10+
useTimeout(fn, delay);
11+
```
12+
13+
| Parameters | Type | Required | Default | Description |
14+
| ---------- | ---------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
15+
| fn | `Function` | `true` | | A function to be executed after the timer expires. |
16+
| delay | `Number` | `false` | `0 ` | The time, in milliseconds (thousandths of a second), the timer should wait before the specified function is executed. |
17+
18+
## State
19+
20+
The `useTimeout` function exposes the following reactive state:
21+
22+
```js
23+
import { useTimeout } from "vue-composable";
24+
25+
const { ready } = useTimeout(fn, delay);
26+
```
27+
28+
| State | Type | Description |
29+
| ----- | ------------ | ----------- | ---------------------------------------------------------------------------------------------------- |
30+
| ready | `Ref<Boolean | null>` | current timeout state:<br/>&emsp;false - pending <br/>&emsp;true - called <br/>&emsp;null - canceled |
31+
32+
## Methods
33+
34+
The `useTimeout` function exposes the following methods:
35+
36+
```js
37+
import { useTimeout } from "vue-composable";
38+
39+
const { cancel } = useTimeout(fn, delay);
40+
```
41+
42+
| Signature | Description |
43+
| --------- | ------------------ |
44+
| `cancel` | cancel the timeout |
45+
46+
## Example
47+
48+
<timeout-example s/>
49+
50+
### Code
51+
52+
```vue
53+
<template>
54+
<div>
55+
<p />
56+
<button @click="show">Show an alert box after two seconds</button>
57+
<p />
58+
<button @click="cancel">Cancel alert before it happens</button>
59+
</div>
60+
</template>
61+
<script>
62+
import { useTimeout } from "vue-composable";
63+
export default {
64+
setup() {
65+
let cancelTimeout;
66+
67+
function show() {
68+
const { cancel } = useTimeout(() => {
69+
alert("useTimeout callback invoked");
70+
}, 2000);
71+
cancelTimeout = cancel;
72+
}
73+
74+
function cancel() {
75+
cancelTimeout();
76+
}
77+
78+
return {
79+
show,
80+
cancel,
81+
};
82+
},
83+
};
84+
</script>
85+
```
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { useTimeout } from "../../src";
2+
import { createVue } from "../utils";
3+
import { Ref, ref } from "../../src/api";
4+
5+
describe("timeout", () => {
6+
jest.useFakeTimers();
7+
it("should be defined", () => {
8+
expect(useTimeout).toBeDefined();
9+
});
10+
11+
it("should call passed function after given amount of time", async () => {
12+
let count = 0;
13+
useTimeout(() => {
14+
count++;
15+
}, 1000);
16+
17+
expect(count).toBe(0);
18+
jest.advanceTimersByTime(900);
19+
// should not be resolved
20+
expect(count).toBe(0);
21+
22+
jest.advanceTimersByTime(100);
23+
expect(count).toBe(1);
24+
});
25+
26+
it("should set ready true after run callback", async () => {
27+
const { ready } = useTimeout(() => {}, 1000);
28+
29+
expect(ready.value).toBe(false);
30+
jest.advanceTimersByTime(1000);
31+
expect(ready.value).toBe(true);
32+
});
33+
34+
it("should cancel function call when call cancel function", async () => {
35+
let count = 0;
36+
const { ready, cancel } = useTimeout(() => {
37+
count++;
38+
}, 1000);
39+
40+
expect(ready.value).toBe(false);
41+
expect(count).toBe(0);
42+
43+
cancel();
44+
45+
jest.advanceTimersByTime(1000);
46+
expect(ready.value).toBe(null);
47+
expect(count).toBe(0);
48+
});
49+
50+
it("should cancel on unMounted", async () => {
51+
let ready: Ref<boolean | null> = ref(false);
52+
53+
const { mount, destroy } = createVue({
54+
template: `<div></div>`,
55+
setup() {
56+
ready = useTimeout(() => {}, 1000).ready;
57+
},
58+
});
59+
60+
mount();
61+
62+
expect(ready.value).toBe(false);
63+
jest.advanceTimersByTime(500);
64+
expect(ready.value).toBe(false);
65+
66+
destroy();
67+
expect(ready.value).toBe(null);
68+
});
69+
70+
it("should default the delay to 0", () => {
71+
let count = 0;
72+
useTimeout(() => {
73+
count++;
74+
});
75+
76+
expect(count).toBe(0);
77+
jest.runOnlyPendingTimers();
78+
expect(count).toBe(1);
79+
});
80+
});

packages/vue-composable/src/web/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ export * from "./cssVariables";
1111
export * from "./worker";
1212
export * from "./share";
1313
export * from "./clipboard";
14+
export * from "./timeout";
1415
export { useWorkerFunction, WebWorkerFunctionOptions } from "./workerFunction";
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ref, Ref, onUnmounted } from "../api";
2+
3+
interface UseTimeoutReturn {
4+
/**
5+
* current timeout state:
6+
* false - pending
7+
* true - called
8+
* null - canceled
9+
*/
10+
ready: Ref<boolean | null>;
11+
/**
12+
* cancel the timeout
13+
*/
14+
cancel: () => void;
15+
}
16+
/**
17+
* @param fn setTimeout callback
18+
* @param delay If this parameter is omitted, a value of 0 is used
19+
* (https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout)
20+
*/
21+
export function useTimeout(
22+
fn: () => void,
23+
delay: number = 0
24+
): UseTimeoutReturn {
25+
let ready: Ref<boolean | null> = ref(false);
26+
27+
const timeoutId = setTimeout(() => {
28+
ready.value = true;
29+
fn();
30+
}, delay);
31+
32+
const cancel = () => {
33+
ready.value = null;
34+
clearTimeout(timeoutId);
35+
};
36+
37+
onUnmounted(cancel);
38+
39+
return {
40+
ready,
41+
cancel,
42+
};
43+
}

0 commit comments

Comments
 (0)