-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(useFetch): Adding useFetch function
- Loading branch information
1 parent
8f77a5a
commit cbf3cfa
Showing
11 changed files
with
1,327 additions
and
635 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './useFetch' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
<template> | ||
<div> | ||
<div class="actions"> | ||
<button | ||
class="button is-primary" | ||
@click="startWithSuccess" | ||
:disabled="isLoading" | ||
v-text="isInit ? 'Fetch again!' : 'Fetch'" | ||
/> | ||
<button | ||
class="button is-info" | ||
@click="startWithFailed" | ||
:disabled="isLoading" | ||
v-text="`Fetch with failure`" | ||
/> | ||
<button | ||
class="button is-danger" | ||
@click="stop" | ||
:disabled="!isLoading" | ||
v-text="`Abort fetch`" | ||
/> | ||
</div> | ||
|
||
<!-- isAborted --> | ||
<use-fetch-demo-table status="Aborted" v-if="isAborted"> | ||
Resource fetch aborted with status code | ||
<strong>{{ status }}</strong> and message | ||
<strong>{{ statusText }}</strong> | ||
</use-fetch-demo-table> | ||
|
||
<!-- isFailed --> | ||
<use-fetch-demo-table status="Failed" v-else-if="isFailed"> | ||
Resource fetch failed with status code | ||
<strong>{{ status }}</strong> and message | ||
<strong>{{ statusText }}</strong> | ||
</use-fetch-demo-table> | ||
|
||
<!-- isSuccess --> | ||
<div v-else> | ||
<!-- !isInit --> | ||
<use-fetch-demo-table status="Not initialized" v-if="!isInit"> | ||
Click "Fetch" to initialize the request. | ||
</use-fetch-demo-table> | ||
|
||
<!-- isLoading --> | ||
<use-fetch-demo-table status="Loading" v-else-if="isLoading"> | ||
Resource is being fetched... | ||
</use-fetch-demo-table> | ||
|
||
<!-- Fetched --> | ||
<use-fetch-demo-table status="Success" v-else> | ||
<p> | ||
Resource fetched successfully with status code | ||
<strong>{{ status }}</strong> and message | ||
<strong>{{ statusText }}</strong> | ||
</p> | ||
<img class="img" :src="data.message" alt="" /> | ||
</use-fetch-demo-table> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import { ref } from '@src/api' | ||
import { useFetch } from '@src/vue-use-kit' | ||
import UseFetchDemoTable from './UseFetchDemoTable.vue' | ||
export default Vue.extend({ | ||
name: 'UseFetchDemo', | ||
components: { UseFetchDemoTable }, | ||
setup() { | ||
const isInit = ref(false) | ||
const slowApi = 'http://slowwly.robertomurray.co.uk/delay/1000/url' | ||
const randomDogUrl = `${slowApi}/https://dog.ceo/api/breeds/image/random` | ||
const url = ref(randomDogUrl) | ||
const { | ||
data, | ||
status, | ||
statusText, | ||
isLoading, | ||
isFailed, | ||
isAborted, | ||
start, | ||
stop | ||
} = useFetch(url, {}, false) | ||
const startWithSuccess = () => { | ||
isInit.value = true | ||
url.value = randomDogUrl | ||
start() | ||
} | ||
const startWithFailed = () => { | ||
isInit.value = true | ||
url.value = `${slowApi}/https://dog.ceo` | ||
start() | ||
} | ||
return { | ||
data, | ||
status, | ||
statusText, | ||
isInit, | ||
isLoading, | ||
isFailed, | ||
isAborted, | ||
startWithSuccess, | ||
startWithFailed, | ||
stop | ||
} | ||
} | ||
}) | ||
</script> | ||
|
||
<style scoped> | ||
.actions { | ||
padding-bottom: 20px; | ||
} | ||
.img { | ||
display: block; | ||
margin: 20px 0 0; | ||
max-width: 300px; | ||
border-radius: 3px; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<template> | ||
<table class="table is-fullwidth"> | ||
<thead> | ||
<tr> | ||
<th>Status</th> | ||
<th>Message</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
<tr> | ||
<td v-text="status" /> | ||
<td width="100%"><slot></slot></td> | ||
</tr> | ||
</tbody> | ||
</table> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
export default Vue.extend({ | ||
name: 'UseFetchDemoTable', | ||
props: { | ||
status: String | ||
} | ||
}) | ||
</script> | ||
|
||
<style scoped></style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
# useFetch | ||
|
||
Vue function that fetch resources asynchronously across the network. | ||
|
||
## Reference | ||
|
||
```typescript | ||
type TUseFetchUrl = RequestInfo | Ref<RequestInfo> | ||
``` | ||
```typescript | ||
function useFetch( | ||
url: TUseFetchUrl, | ||
options?: RequestInit, | ||
runOnMount?: boolean | ||
): { | ||
data: Ref<any> | ||
status: Ref<number | null> | ||
statusText: Ref<string | null> | ||
isLoading: Ref<boolean> | ||
isFailed: Ref<boolean> | ||
isAborted: Ref<boolean> | ||
start: () => Promise<void> | ||
stop: () => void | ||
} | ||
``` | ||
|
||
### Parameters | ||
|
||
- `url: TUseFetchUrl` the fetch url value, can be type string or type `RequestInfo`. | ||
- `options: RequestInit` the fetch url options. | ||
- `runOnMount: boolean` whether to fetch on mount, `true` by default. | ||
|
||
### Returns | ||
|
||
- `data: Ref<any>` the response data, has to be of JSON type otherwise will return an error | ||
- `status: Ref<number | null>` the status code of the response | ||
- `statusText: Ref<string | null>` the status text of the response | ||
- `isLoading: Ref<boolean>` whether fetch request is loading or not | ||
- `isFailed: Ref<boolean>` whether fetch request has failed or not | ||
- `isAborted: Ref<boolean>` whether fetch request has been aborted or not | ||
- `start: Function` the function used for starting fetch request | ||
- `stop: Function` the function used for aborting fetch request | ||
|
||
## Usage | ||
|
||
```html | ||
<template> | ||
<div> | ||
<div v-if="isFailed">Failed!</div> | ||
<div v-else-if="isLoading">Loading...</div> | ||
<div v-else><img :src="data.message" alt="" /></div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import Vue from 'vue' | ||
import { useFetch } from 'vue-use-kit' | ||
export default Vue.extend({ | ||
name: 'UseFetchDemo', | ||
setup() { | ||
const url = 'https://dog.ceo/api/breeds/image/random' | ||
const { data, isLoading, isFailed } = useFetch(url) | ||
return { data, isLoading, isFailed } | ||
} | ||
}) | ||
</script> | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { storiesOf } from '@storybook/vue' | ||
import path from 'path' | ||
import StoryTitle from '@src/helpers/StoryTitle.vue' | ||
import UseFetchDemo from './UseFetchDemo.vue' | ||
|
||
const functionName = 'useFetch' | ||
const functionPath = path.resolve(__dirname, '..') | ||
const notes = require(`./${functionName}.md`).default | ||
|
||
const basicDemo = () => ({ | ||
components: { StoryTitle, demo: UseFetchDemo }, | ||
template: ` | ||
<div class="container"> | ||
<story-title | ||
function-path="${functionPath}" | ||
source-name="${functionName}" | ||
demo-name="UseFetchDemo.vue" | ||
> | ||
<template v-slot:title></template> | ||
<template v-slot:intro></template> | ||
</story-title> | ||
<demo /> | ||
</div>` | ||
}) | ||
|
||
storiesOf('side effects|useFetch', module) | ||
.addParameters({ notes }) | ||
.add('Demo', basicDemo) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// import { mount } from '@src/helpers/test' | ||
// import { useFetch } from '@src/vue-use-kit' | ||
|
||
afterEach(() => { | ||
jest.clearAllMocks() | ||
}) | ||
|
||
describe('useFetch', () => { | ||
it('should do something', () => { | ||
// Add test here | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { ref, onMounted, onUnmounted, Ref, isRef } from '@src/api' | ||
|
||
export type TUseFetchUrl = RequestInfo | Ref<RequestInfo> | ||
|
||
const abortError = 'AbortError' | ||
|
||
const isContentTypeJson = (res: Response) => { | ||
const contentType = res.headers.get('content-type') | ||
return contentType && contentType.includes('application/json') | ||
} | ||
|
||
const getUrl = (url: TUseFetchUrl) => (isRef(url) ? url.value : url) | ||
|
||
const fetchWrapper = async (url: TUseFetchUrl, opts: RequestInit) => { | ||
const res = await fetch(getUrl(url), opts) | ||
const resData = isContentTypeJson(res) && (await res.json()) | ||
return { resData, res } | ||
} | ||
|
||
export function useFetch( | ||
url: TUseFetchUrl, | ||
options: RequestInit = {}, | ||
runOnMount = true | ||
) { | ||
const data: Ref<any> = ref(null) | ||
const status: Ref<null | number> = ref(null) | ||
const statusText: Ref<null | string> = ref(null) | ||
const isLoading = ref(false) | ||
const isFailed = ref(false) | ||
const isAborted = ref(false) | ||
|
||
let controller: AbortController | ||
|
||
const start = async () => { | ||
try { | ||
controller = new AbortController() | ||
const signal = controller.signal | ||
|
||
isLoading.value = true | ||
isFailed.value = false | ||
isAborted.value = false | ||
|
||
const { resData, res } = await fetchWrapper(getUrl(url), { | ||
signal, | ||
...options | ||
}) | ||
data.value = resData | ||
|
||
isLoading.value = false | ||
isFailed.value = !res.ok | ||
status.value = res.status | ||
statusText.value = res.statusText | ||
} catch (err) { | ||
if (err.name === abortError) isAborted.value = true | ||
isLoading.value = false | ||
isFailed.value = true | ||
status.value = 500 | ||
statusText.value = err.message || err.name | ||
} | ||
} | ||
|
||
const stop = () => { | ||
if (controller) controller.abort() | ||
} | ||
|
||
onMounted(() => runOnMount && start()) | ||
onUnmounted(stop) | ||
|
||
return { | ||
data, | ||
status, | ||
statusText, | ||
isLoading, | ||
isFailed, | ||
isAborted, | ||
start, | ||
stop | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters