react-cancelable
Internet traffic economizer
Table of Contents
- Motivation
- Instalation
- Tools
- useCancelableReq
- useCancelableImg
- cancelable HOF
- Fetch vs Axios
- Best practices (WIP)
Motivation
In most of cases client consumes a lot of excess internet traffic. Modern web applications make a huge bunch of requests per conventional time unit then a lot of clients don't wait until all requests made by web app are finished. As a result, the browser expects data that will no longer be used
But don't worry you can easily deal with it with the latest AbortController API and react-cancelable
Instalation
npm install react-cancelable
yarn add react-cancelable
Before installation be sure you have installed the required peer dependencies to your project
{
"react": "^17.0.0",
}
Tools
useCancelableReq
Make cancelable request. Hook helps you to control request canceling by React Component Lifecycle or by your own.
Signature
type RequestFn = (controller: AbortController) => Promise<any>
type Opts = {
isLazy?: boolean;
cancelOnUnmount?: boolean;
controller?: AbortController;
onComplete?: (res: any) => void;
onFail?: (error: any) => void
onCancel?: VoidFunction;
}
type Artefacts = {
res?: Response;
data?: any;
error?: any;
isLoading: boolean;
cancel: VoidFunction,
makeLazyRequest: VoidFunction | null;
}
useCancelableReq(fn: RequestFn, opts?: Opts): Artefacts
API
Name | Description | Default |
---|
isLazy | Control request by your own if true. By default, a request will be made on the component mount | false |
cancelOnUnmount | Request will be canceled on component unmount if true | true |
controller | By default component will create instance automaticaly under the hood. If yoo want to controll multiple requests with one conteroller pass your own instance of AbortControler | undefined |
onComplete | Trigger after request is completed | undefined |
onFail | Trigger after request is failed | undefined |
onCancel | Trigger after request is canceled | undefined |
res | Response object | undefined |
data | Payload of a request | undefined |
error | Error of a request | undefined |
isLoading | Flag to determine active status of request. If isLazy is true isLoading is false by default | true |
cancel | Request cancel trigger | function |
makeLazyRequest | Make request trigger. If isLazy is true makeLazyRequest is function | null |
Example
import React from 'react'
import { useCancelableReq } from 'react-cancelable'
function makeRequest(controller) {
return fetch("YOUR_ENDPOINT", { signal: controller.signal })
}
function Item() {
const { data, isLoading, error } = useCancelableReq(makeRequest)
return (
<>
{isLoading && <span>Loading...</span>}
{error && <span>Error occured</span>}
{data && <span>Data is fetched</span>}
</>
)
}
useCancelableImg
Make cancelable request. Hook helps you to cancel requested image.
Signature
type RequestFn = (controller: AbortController) => Promise<any>
type Opts = {
isLazy?: boolean;
cancelOnUnmount?: boolean;
controller?: AbortController;
onComplete?: (res: any) => void;
onFail?: (error: any) => void
onCancel?: VoidFunction;
}
type Artefacts = {
res?: Response;
src?: string;
error?: any;
isLoading: boolean;
cancel: VoidFunction,
makeLazyRequest: VoidFunction | null;
}
useCancelableImg(fn: RequestFn, opts?: Opts): Artefacts
API
Name | Description | Default |
---|
isLazy | Control request by your own if true. By default, a request will be made on the component mount | false |
cancelOnUnmount | Request will be canceled on component unmount if true | true |
controller | By default component will create instance automaticaly under the hood. If yoo want to controll multiple requests with one conteroller pass your own instance of AbortControler | undefined |
onComplete | Trigger after request is completed | undefined |
onFail | Trigger after request is failed | undefined |
onCancel | Trigger after request is canceled | undefined |
res | Response object | undefined |
src | Generated ObjectURI to Blob image after request done | undefined |
error | Error of a request | undefined |
isLoading | Flag to determine active status of request. If isLazy is true isLoading is false by default | true |
cancel | Request cancel trigger | function |
makeLazyRequest | Make request trigger. If isLazy is true makeLazyRequest is function | null |
Example
import React from 'react'
import { useCancelableReq } from 'react-cancelable'
function getImage(controller) {
return fetch('IMAGE_URL', { signal: controller.signal })
}
function Item() {
const { src, isLoading, error } = useCancelableImg(getImage)
return (
<>
{isLoading && <span>Loading...</span>}
{src && <img src={src} />}
</>
)
}
cancelable HOF
Hight order function to create cancelable requests
Signature
type RequestFn = (controller: AbortController) => Promise<any>
type RequestPromise = Promise<any> & { cancel: VoidFunction }
cancelable(fn: RequestFn, controller?: AbortController): RequestPromise
API
Name | Description | Default |
---|
fn | Callback that returns Promise generated by HTTP client | function |
controller | By default component will create instance automaticaly under the hood. If yoo want to controll multiple requests with one conteroller pass your own instance of AbortControler | undefined |
cancel | Request cancel trigger. Property added to returned Promise | function |
Example
import { cancelable } from 'react-cancelable'
function makeRequest(controller) {
return fetch("YOUR_ENDPOINT", { signal: controller.signal })
}
const request = cancelable(makeRequest)
setTimeout(() => {
request.cancel()
}, 1000)
Fetch vs Axios
There is no difference what HTTP client you use. Package have one important rule - HTTP client must accept AbortController signal.
function makeFetchRequest(controller) {
return fetch("YOUR_ENDPOINT", { signal: controller.signal })
}
function makeAxiosRequest(controller) {
return axios.get("YOUR_ENDPOINT", { signal: controller.signal })
}
Best practices (WIP)
Cancel multiple similar request via one AbortController. Each helper can take controller
parameter.
import { cancelable } from 'react-cancelable'
const controller = new AbortController()
function makeRequest(controller) {
return fetch("YOUR_ENDPOINT", { signal: controller.signal })
}
new Array(100).fill(0).forEach(() => { cancelable(makeRequest, controller) } )
setTimeout(() => {
controller.abort()
}, 1000)