Fetch + Abort Controller + timeouts + React.js
window.fetch
is awesome, but it lacks timeouts. You can't just supply a value in milliseconds and expect the fetch request to be aborted if there is no response from the server within that time frame.
// This timeouts after 3000s, stops listening for a response from the server.
fetch('https://your-url.com', {
timeoutDuration: 3000
})
// THIS DOES NOT EXIST
Something like this would have been great! But that doesn't exist.
We as web developers have a solution for this i.e., the AbortController. Although we can't pass a timeout duration, we can pass a signal and cancel the fetch. So let's say we want to set a 1sec duration after which our fetch request aborts. This is how we would do it:
const controller = new AbortController()
const signal = controller.signal
fetch('https://abc.xyz', { signal })
// Abort the fetch request after 1s
setTimeout(() => contoller.abort(), 1000)
It's perfectly fine if just want to send requests, but when you try to use something like this with a react component that shows some UI indicating the current status of the request, you can imagine the kind of mess a new developer can make. Let's say the component has a button and a text. 3 states:
- text: READY, button: clickable
- text: WAITING/LOADING, button: disabled, shows a spinner
- text: LOADING COMPLETE, button: clickable
How would you do this? Post your solutions in the comments below.
I would like to have something like:
const { data, loading, error, send } = useSomething(fetchParameters)
That's right, I won't waste a second before abstracting the mess away from the component. I would also expect these values to update in realtime so that I can use them to update my component.
For those who are black-belt in REACT-FOO, and have already figured it out, it's available on npm:
react-confetch
I also plan on adding it as a GitHub Package soon.
If you are new to react, here are the setup steps:
- Add the package as a dependency to your project:
npm install --save react-confetch
- Wrap your app component with
ConfetchContext
like this:
import './index.css'
import { ConfetchContext } from 'react-confetch'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
const globalFetchConfig = {
timeoutDuration: 3000,
}
ReactDOM.render((
<ConfetchContext.Provider value={globalFetchConfig}>
<App />
</ConfetchContext.Provider>
), document.getElementById('root'))
Notice the globalFetchConfig
? That is where you would add all the default configuration values for all fetch requests made using this package. Use the useConfetch
hook in your components like this:
-app.js
import React from 'react'
import { useConfetch } from 'react-confetch'
const App = () => {
const convertResponseToImageData = res => res.blob().then(image => URL.createObjectURL(image))
const config = {
url: 'https://avatars.githubusercontent.com',
endpoint: '/akshay-nm',
body: null,
method: 'GET',
timeoutDuration: 10,
onResponse: convertResponseToImageData, // this is where you add logic to handle the response, any return value will be set as data
// onError: err => {}, // you can pass an error handler too, any return values will be assigned to error
// any error thrown is returned as error
}
const { data, loading, error, send } = useConfetch(config)
return (
<div>
<div>{data && <img src={data} alt='Image' />}</div>
<div>loading: {loading? 'yes' : 'no'}</div>
<div>error: {error? error.message: '-'}</div>
<div>
<button onClick={send}>Send a fetch request</button>
</div>
</div>
)
}
export default App
It's just a wrapper on the whole abort functionality we talked about earlier.
I'll add some examples on how to wrap this hook for each endpoint you want to access as the component still has some extra code that can be moved elsewhere. If you are doing the same, please share some of that inspiring code!
Till next time…
Top comments (1)
I think this is outdated now?