react-fetch
React hooks based fetching with cache and abortcontroller support
Table of contents
Features
- Create custom fetch hooks from (axios) resources/services
- Auto abort fetch requests
- Memoize fetch responses
Installing
Using yarn:
yarn add @acato/react-fetch
Using npm:
npm i @acato/react-fetch
Example
First, create a (axios) resource:
export const myResource = createResource(...)
createResource
is a function from the @acato/axios module.
Then, define your service, using the resource we have just created.
export const myService = {
getSomething: (signal) => myResource.get('api/something', { signal: signal() }),
postSomething: (data, signal) =>
mockResource.post('api/anything', { ...data }, { signal: signal() })
}
The signal
argument is added by the createFetchHooks function. The custom
hook, created with this function, will automagically abort any pending requests on
'unmounting' or when a new request executes when the last one is still pending.
Next, we'll create our custom hooks:
const handlers = {
response: (res) => res?.data,
error: (err) => {
return err.message
}
}
export const myFetchHooks = createFetchHooks(
{
name: 'useGetSomethingWithCache',
fetcher: myService.getSomething,
memoize: true,
handlers
},
{
name: 'useGetSomethingWithTTLCache',
fetcher: myService.getSomething,
memoize: {
ttl: 10000
},
handlers
},
{
name: 'useGetSomethingWithTTLAndArgsCache',
fetcher: myService.getSomething,
memoize: {
ttl: 10000,
memoizeArgs: true
},
handlers
},
{
name: 'useGetSomething',
fetcher: myService.getSomething,
handlers
},
{ fetcher: myService.postSomething, handlers }
)
The function createFetchHooks
will create a custom hook for each hook-definition
you pass to this function. The only required property is fetcher
, the actual fetch
function. The name
prop is not required, it will be derived from the function name.
The response and error handlers are just there to pre-populate responses and errors, so in the consuming component you dont have to grab the props you need (like: response?.data?.data). You could also trigger some custom logic in there as well, like setting some global state, notifiers, etc.
myFetchHooks
will have these properties:
- useGetSomethingWithCache: after 1st fetch it will always be resolved from cache.
- useGetSomethingWithTTLCache: will be resolved from cache only if ttl is valid.
- useGetSomethingWithTTLAndArgsCache: will cache the response for each new args and will be resolved from cache if the ttl is still valid.
- useGetSomething: will always be fetched, no caching used.
- usePostSomething: will never be cached.
Now we are ready to go and use our new fetch hooks!
const myComponent = () => {
const [getSomething, loading, something, err] = useGetSomething()
useEffect(() => {
getSomething()
}, [getSomething])
// with arguments?
useEffect(() => {
getSomething(arg)
}, [getSomething, arg])
}
API
Fetch hooks can be made by passing relevant hook-configs to createFetchHooks
.
createFetchHooks(...hookConfig)
Hook config
{
name: 'useGetPosts', // name is not required, hook name can be derived from the `fetcher` property.
fetcher: myService.getPosts, // Required. This is the fetch function used in the hook. It should return a `Promise`
// memoize: boolean or object. Not required.
memoize: true,
memoize: {
ttl: 10000, // Time to live, max age of cached response
memoizeArgs: true, // If get requests require argument(s), each request will be cached with args as the cache key.
},
// handlers: Object, not required.
// Handler functions can be used to make it easier to handle api responses in a generic way.
handlers: {
response: res => res?.data,
error: err => err?.message
}
}
Hook return value
The hook function, once invoked, will give you the following return value:
const [fetchFn, loadingState, data, err] = useGetSomething()
fetchFn: The actual fetch function. Stable,
loadingState: Indicates if the request is pending or finished,
data: Depending on using `handlers` or not, it will hold the fetch response,
err: Depending on using `handlers` or not, it will hold the error state,