Skip to content

Commit ec09e77

Browse files
authored
feat: add useFetch (#15)
* feat: fetch with abort implementation * docs: added fetch to docs * docs: added usage snippet for fetch * docs: remove comment from fetch snippet
1 parent e625dde commit ec09e77

File tree

5 files changed

+136
-14
lines changed

5 files changed

+136
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Each composition function is designed to degrade gracefully so you can safely us
3535
- [Device Light](https://logaretm.github.io/vue-use-web/guide/device-light.md).
3636
- [Device Motion](https://logaretm.github.io/vue-use-web/guide/device-motion.md).
3737
- [Device Orientation](https://logaretm.github.io/vue-use-web/guide/device-orientation.md).
38+
- [Fetch API](https://logaretm.github.io/vue-use-web/guide/fetch.md).
3839
- [Full-screen](https://logaretm.github.io/vue-use-web/guide/fullscreen.md).
3940
- [Geo-location API](https://logaretm.github.io/vue-use-web/guide/geolocation.md).
4041
- [Local-storage API](https://logaretm.github.io/vue-use-web/guide/local-storage.md)
@@ -47,7 +48,6 @@ Each composition function is designed to degrade gracefully so you can safely us
4748
- [Window Scroll Position](https://logaretm.github.io/vue-use-web/guide/scroll-position.md).
4849
- [Window Size](https://logaretm.github.io/vue-use-web/guide/window-size.md).
4950
- Bluetooth (WIP).
50-
- Fetch (WIP).
5151
- Local storage (WIP).
5252
- Notification (WIP).
5353
- Share (WIP).

docs/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ npm install @vue/composition-api vue-use-web
4242
- [Device Light](./guide/device-light.md).
4343
- [Device Motion](./guide/device-motion.md).
4444
- [Device Orientation](./guide/device-orientation.md).
45+
- [Fetch API](./guide/fetch.md).
4546
- [Full-screen](./guide/fullscreen.md).
4647
- [Geo-location API](./guide/geolocation.md).
4748
- [Local-storage API](./guide/local-storage.md)
@@ -54,7 +55,6 @@ npm install @vue/composition-api vue-use-web
5455
- [Window Scroll Position](./guide/scroll-position.md).
5556
- [Window Size](./guide/window-size.md).
5657
- Bluetooth <Badge text="WIP" type="warn" />.
57-
- Fetch <Badge text="WIP" type="warn" />.
5858
- Local storage <Badge text="WIP" type="warn" />.
5959
- Notification <Badge text="WIP" type="warn" />.
6060
- Share <Badge text="WIP" type="warn" />.

docs/guide/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ These are the currently implemented Web APIs and the planned ones.
2626
- [Device Light](./device-light.md).
2727
- [Device Motion](./device-motion.md).
2828
- [Device Orientation](./device-orientation.md).
29+
- [Fetch API](./fetch.md).
2930
- [Full-screen](./fullscreen.md).
3031
- [Geo-location API](./geolocation.md).
3132
- [Intersection Observer](./intersection-observer.md).
@@ -38,7 +39,6 @@ These are the currently implemented Web APIs and the planned ones.
3839
- [Window Scroll Position](./scroll-position.md).
3940
- [Window Size](./window-size.md).
4041
- Bluetooth <Badge text="WIP" type="warn" />.
41-
- Fetch <Badge text="WIP" type="warn" />.
4242
- Local storage <Badge text="WIP" type="warn" />.
4343
- Notification <Badge text="WIP" type="warn" />.
4444
- Share <Badge text="WIP" type="warn" />.

docs/guide/fetch.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# Fetch
2+
3+
> The [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch).
4+
5+
## State
6+
7+
The `useFetch` function exposes the following reactive state:
8+
9+
```js
10+
import { useFetch } from 'vue-use-web';
11+
12+
const { isLoading, json, text, error, success } = useFetch('http://myurl.com');
13+
```
14+
15+
| State | Type | Description |
16+
| --------- | --------- | -------------------------------------------------------------- |
17+
| isLoading | `Boolean` | If the request is pending. |
18+
| error | `Boolean` | If the request resulted in a non 200 status code. |
19+
| success | `Boolean` | If the request is successful. i.e resulted in 200 status code. |
20+
| json | `any` | The response body as JSON. |
21+
| text | `string` | The raw response body as a string. |
22+
23+
## Methods
24+
25+
The `useFetch` function exposes the following methods:
26+
27+
```js
28+
import { useFetch } from 'vue-use-web';
29+
30+
const { cancel } = useFetch(elem);
31+
```
32+
33+
| Signature | Description |
34+
| --------- | ----------------------------------------------------------------------------------------------------------------------------------- |
35+
| `cancel` | Cancels the fetch request if browser supports `AbortController`, otherwise the request will complete but will not update the state. |
36+
37+
## Example
38+
39+
```vue
40+
<template>
41+
<div>
42+
<div>{{ isLoading }}</div>
43+
<div>{{ success }}</div>
44+
<div>{{ text }}</div>
45+
<div>{{ blob }}</div>
46+
<div>{{ json }}</div>
47+
<div>{{ cancelled }}</div>
48+
<button @click="cancel">Cancel Request</button>
49+
</div>
50+
</template>
51+
52+
<script>
53+
import { useFetch } from "vue-use-web";
54+
55+
export default {
56+
setup() {
57+
const {
58+
isLoading,
59+
error,
60+
success,
61+
cancel,
62+
text,
63+
blob,
64+
json,
65+
cancelled
66+
} = useFetch("/data.json");
67+
68+
return { isLoading, error, success, cancel, text, blob, json, cancelled };
69+
}
70+
};
71+
</script>
72+
```
73+
74+
TODO: useFetch example

src/Fetch.ts

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,77 @@
11
import { reactive, toRefs, onMounted } from '@vue/composition-api';
22

33
export function useFetch(url: RequestInfo, options: RequestInit) {
4-
const state = reactive({
4+
const stateDefs: {
5+
blob: Blob | null;
6+
json: any;
7+
text: string;
8+
isLoading: boolean;
9+
success: boolean;
10+
error: boolean;
11+
cancelled: boolean;
12+
} = {
513
isLoading: false,
614
success: false,
715
error: false,
8-
response: null
9-
});
16+
cancelled: false,
17+
json: null,
18+
blob: null,
19+
text: ''
20+
};
21+
22+
const state = reactive(stateDefs);
23+
24+
let signal: AbortSignal | undefined;
25+
let controller: AbortController;
26+
if (typeof AbortController !== 'undefined') {
27+
controller = new AbortController();
28+
signal = controller.signal;
29+
}
1030

11-
onMounted(() => {
12-
window
13-
.fetch(url, options)
31+
function execute() {
32+
state.isLoading = true;
33+
34+
return window
35+
.fetch(url, { signal, ...options })
1436
.then(res => {
1537
state.success = res.ok;
1638
state.error = !res.ok;
1739
state.isLoading = false;
1840

19-
return res.json();
41+
return Promise.all([res.clone().text(), res.clone().blob(), res.json()]);
42+
})
43+
.then(([text, blob, json]) => {
44+
// Graceful degradation for cancellation.
45+
if (state.cancelled) {
46+
return;
47+
}
48+
49+
state.text = text;
50+
state.blob = blob;
51+
state.json = json;
2052
})
21-
.then(json => {
22-
state.response = json;
53+
.catch(() => {
54+
state.error = true;
55+
state.isLoading = false;
2356
});
24-
});
57+
}
58+
59+
onMounted(execute);
60+
61+
function cancel() {
62+
if (state.success) {
63+
return;
64+
}
65+
66+
state.cancelled = true;
67+
if (controller) {
68+
controller.abort();
69+
}
70+
}
2571

2672
return {
27-
...toRefs(state)
73+
...toRefs(state),
74+
cancel,
75+
execute
2876
};
2977
}

0 commit comments

Comments
 (0)