Skip to content

Commit

Permalink
Abort and demos (#14)
Browse files Browse the repository at this point in the history
* abort and demos

* maybe idempotent-babel-polyfill

* remove dist

* Update README.md

* wrapping up, finished demos, pulled out unused code

* v0.1.50

* v0.1.51

* making it so dist is only included when we publish

* switch package name, adding build step in publish

* cleaning up examples

* Update README.md

* Update README.md

* Create remove.js

* Add files via upload

* Delete remove.js

* Update README.md
  • Loading branch information
alex-cory committed Apr 28, 2019
1 parent f5831b8 commit 323fa78
Show file tree
Hide file tree
Showing 9 changed files with 311 additions and 328 deletions.
14 changes: 10 additions & 4 deletions README.md
Expand Up @@ -93,17 +93,21 @@ patch({
})
```
#### Coming Soon: `abort`
#### Abort
<img src="public/abort-example-1.gif" height="250" />
```jsx
const githubRepos = useFetch({
baseUrl: `https://api.github.com/search/repositories`
baseUrl: `https://api.github.com/search/repositories?q=`
})

const searchGithub = e => githubRepos.get(`?q=${e.target.value || "''"}`)
// the line below is not isomorphic, but for simplicity we're using the browsers `encodeURI`
const searchGithubRepos = e => githubRepos.get(encodeURI(e.target.value))

<>
<input onChange={searchGithub} />
<input onChange={searchGithubRepos} />
<button onClick={githubRepos.abort}>Abort</button>
{githubRepos.loading ? 'Loading...' : githubRepos.data.items.map(repo => (
<div key={repo.id}>{repo.name}</div>
Expand Down Expand Up @@ -183,6 +187,8 @@ Todos
- [ ] Allow option to fetch on server instead of just having `loading` state
- [ ] Allow option for callback for response.json() vs response.text()
- [ ] add `timeout`
- [ ] add `debounce`
- [ ] if 2nd param of `post` or one of the methods is a `string` treat it as query params
- [ ] error handling if no url is passed
- [ ] tests
- [ ] port to typescript
Expand Down
66 changes: 66 additions & 0 deletions examples/abort-examples.js
@@ -0,0 +1,66 @@
import React from 'react'
import useFetch from '../src/index'
import { delayedUrl } from './urls'

/**
* Abortable demos
* - https://github.com/dai-shi/react-hooks-async
*/

const GithubRepoSearchAbortDemo = () => {
const githubQueryRepoURL = 'https://api.github.com/search/repositories?q='
const githubRepos = useFetch({
baseUrl: githubQueryRepoURL,
})

const searchGithub = e => githubRepos.get(e.target.value || "%27%27")

const githubRepoItems = (githubRepos.data || {}).items || []

return (
<>
<h3>Github Repo Search Demo</h3>
<input onChange={searchGithub} />
{githubRepos.loading ? (
<div style={{display: 'flex'}}>
Loading...
<button onClick={githubRepos.abort}>Cancel Request</button>
</div>
) : githubRepoItems.map(({ id, name, html_url }) => (
<li key={id}><a target="_blank" rel="noreferrer noopener" href={html_url}>{name}</a></li>
))}
</>
)
}

const OnClickAbortDemo = () => {
const request = useFetch(delayedUrl)
return (
<>
<h3>On Click Demo</h3>
<button onClick={() => request.post({ no: 'way' })}>Fetch Data</button>
{request.loading ? (
<div style={{display: 'flex'}}>
Loading...
<button onClick={request.abort}>Cancel Request</button>
</div>
) : (
<code style={{ display: 'block' }}>
<pre>{JSON.stringify(request.data, null, 2)}</pre>
</code>
)}
</>
)
}

const AbortDemos = () => (
<>
<div>If for some reason you aren't getting any responses from these, it's possible you have used your daily limit for api calls to these apis.</div>
<h1>Abort Demos</h1>
<p>Open the network tab in the devtools to see this in action 😛</p>
<GithubRepoSearchAbortDemo />
<OnClickAbortDemo />
</>
)

export default AbortDemos
131 changes: 9 additions & 122 deletions examples/index.js
@@ -1,126 +1,13 @@
import React from 'react';
import { render } from 'react-dom';
// import './index.css';
// import App from './App';
// import * as serviceWorker from './serviceWorker';
import useFetch, { useGet, usePost } from '../src/index'
// import useFetch, { useGet, usePost } from '../dist'

const App = () => {
const [data, loading, error, { get }] = useFetch('https://api.etilbudsavis.dk/v2/dealerfront?country_id=DK')
const handleClick = () => get()

// WORKS 😍
// const [data, loading, error, request] = useFetch({
// url: 'https://jsonplaceholder.typicode.com/posts/1',
// // baseUrl: '', // then you can do: request.post({ url: '/posts', body: {} })
// headers: {
// "Content-type": "application/json; charset=UTF-8"
// },
// // timeout: 1000,
// // onMount: true, // run on component did mount
// })

// const [data, loading, error, request] = useFetch('https://jsonplaceholder.typicode.com/posts/1')

// const { data, loading, error, request, get, post, patch, put, del } = useFetch('https://jsonplaceholder.typicode.com/posts/1')
// const { data, loading, error, request, get, post, patch, put, del } = useFetch({
// baseUrl: 'https://jsonplaceholder.typicode.com/posts'
// })

// useEffect(() => {
// // on component did mount or use 'onMount' option above
// request.get()
// }, [request])

// const [data, loading, error, get] = useGet({
// url: 'https://jsonplaceholder.typicode.com/posts/1'
// })
const handleClick2 = () => {
// get('/1')
// post('/', {
// // params: '?no=way&something=true',
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// patch('/1', {
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// put('/1', {
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// del('/1')
// request.get()
// request.post({
// // params: '?no=way&something=true',
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// request.patch({
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// request.put({
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// request.delete({
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// get()
// post({
// // params: '?no=way&something=true',
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// patch({
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// put({
// title: 'foo',
// body: 'bar',
// userId: 1
// })
// del({
// title: 'foo',
// body: 'bar',
// userId: 1
// })
}


if (error) return 'Error...'
if (loading) return 'Loading...'
import React, { useState, Fragment, useRef } from 'react'
import { render } from 'react-dom'
import AbortExamples from './abort-examples'

function App() {
return (
<div className="App">
<header className="App-header">
<button onClick={handleClick}>CLICK</button>
<code style={{ display: 'block' }}>
<pre>{JSON.stringify(data, null, 2)}</pre>
</code>
</header>
</div>
);
<>
<AbortExamples />
</>
)
}

render(<App />, document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
// serviceWorker.unregister();
//
render(<App />, document.getElementById('root'))
9 changes: 9 additions & 0 deletions examples/urls.js
@@ -0,0 +1,9 @@

export const delayedUrl = 'https://httpbin.org/delay/3'
export const chuckUrl = 'https://api.icndb.com/jokes/random/%3FlimitTo=[nerdy]&escape=javascript' // - handles POST too
// export const allUrl = 'https://km04k9k9x5.codesandbox.io/test.json'
// const baseUrl = 'https://jsonplaceholder.typicode.com'
// const postUrl = baseUrl + '/posts'
// POST https://jsonplaceholder.typicode.com/posts
// const allUrl = postUrl + '/1'
// everything else: https://jsonplaceholder.typicode.com/posts/1
14 changes: 9 additions & 5 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "use-http-test",
"version": "0.1.49",
"name": "use-http",
"version": "0.1.57",
"homepage": "https://codesandbox.io/embed/km04k9k9x5",
"main": "dist/index.js",
"keywords": [
Expand All @@ -22,21 +22,25 @@
"react": "^16.8.0"
},
"dependencies": {
"idempotent-babel-polyfill": "^7.0.0",
"react": "^16.8.6"
},
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@babel/preset-react": "^7.0.0",
"babel-jest": "^24.7.1",
"idempotent-babel-polyfill": "^7.0.0",
"jest": "^24.7.1",
"parcel-bundler": "^1.12.3",
"react-dom": "^16.8.6"
},
"scripts": {
"dev": "parcel examples/index.html --open",
"build": "parcel build --target node ./src/*",
"test": "jest"
}
"test": "jest",
"publish": "yarn build && yarn publish"
},
"files": [
"dist"
]
}
Binary file added public/abort-example-1.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/abort-example-2.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 16 additions & 5 deletions src/useFetch.js
@@ -1,5 +1,5 @@
import 'idempotent-babel-polyfill' // so async await works ;)
import { useEffect, useState, useCallback } from 'react'
import { useEffect, useState, useCallback, useRef } from 'react'

const isObject = obj => Object.prototype.toString.call(obj) === '[object Object]'

Expand Down Expand Up @@ -33,8 +33,14 @@ export function useFetch(arg1, arg2) {
const [data, setData] = useState(null)
const [loading, setLoading] = useState(onMount)
const [error, setError] = useState(null)
const controller = useRef(null)

const fetchData = useCallback(method => async (fArg1, fArg2) => {
if ('AbortController' in window) {
controller.current = new AbortController()
options.signal = controller.current.signal
}

let query = ''
if (isObject(fArg1) && method.toLowerCase() !== 'get') {
options.body = JSON.stringify(fArg1)
Expand All @@ -48,7 +54,7 @@ export function useFetch(arg1, arg2) {
setLoading(true)
const response = await fetch(url + query, {
method,
...options,
...options
})
let data = null
try {
Expand All @@ -58,8 +64,9 @@ export function useFetch(arg1, arg2) {
}
setData(data)
} catch (err) {
setError(err)
if (err.name !== 'AbortError') setError(err)
} finally {
controller.current = null
setLoading(false)
}
},
Expand All @@ -72,13 +79,17 @@ export function useFetch(arg1, arg2) {
const put = useCallback(fetchData('PUT'))
const del = useCallback(fetchData('DELETE'))

const request = { get, post, patch, put, del, delete: del }
const abort = () => {
controller.current && controller.current.abort()
}

const request = { get, post, patch, put, del, delete: del, abort }

useEffect(() => {
if (onMount) request[method.toLowerCase()]()
}, [])

return Object.assign([data, loading, error, request], { data, loading, error, request, ...request })
return Object.assign([data, loading, error, request], { data, loading, error, request, abort, ...request })
}

export default useFetch

0 comments on commit 323fa78

Please sign in to comment.