I'm using axios
for fetching/ posting the data to my server and my current approach looks like this:
redux-thunk
, and an action looks like this:export const getRecords = () => (dispatch, getState, { api }) => {
const type = GET_RECORDS_LOAD;
return dispatch({
type,
promise: api.Records.get({ type }),
});
};
api.Record.get
looks like this:import _ from 'lodash';
import axios from 'axios';
const APIInstance = axios.create({
baseURL: process.env.API_URL,
});
const getCancelToken = id => new axios.CancelToken((c) => {
const cancel = c;
const cancelationTokens = _.get(window, 'cancelationTokens', {});
cancelationTokens[id] = cancel;
_.set(window, 'cancelationTokens', cancelationTokens);
});
const api = {
Records: {
get: ({ type }) => APIInstance.get('/my-records', { cancelToken: getCancelToken(type) }),
},
};
Here I create a cancelToken
based on the redux action type and I'm storing it in a window.cancelationTokens
object, so they can be cancelled anywhere from the app.
componentWillUnmount
import * as Types from './path/to/action/types';
const cancelToken = (type) => {
const cancel = _.get(window, `cancelationTokens.${type}`);
if (!cancel) return;
cancel();
}
componentWillUnmount() {
cancelToken(Types.GET_RECORDS_LOAD);
// If more request I have to cancel them manually...
}
As you can see, there is no major problem with this approach, but if I do plenty of requests on one page, I have to cancel them all manually in the componentWillUnmount
.
My questions:
One signal from the AbortController() object can be used to abort/cancel multiple API requests. This means that if we have more than one API request in our component(regardless of the method, POST, GET, DELETE e.t.c), we can use one source variable to cancel all the requests.
You can wrap XItem component with React. memo: const XItem = (props) => { ... } const areEqual = (prevProps, nextProps) => { /* Add your logic here to check if you want to rerender XItem return true if you don't want rerender return false if you want a rerender */ } export default React.
We can use the AbortController object and the associated AbortSignal with the Fetch API to make cancelable HTTP requests. Once the AbortSignal is sent, the HTTP request is canceled and won't be sent if the cancellation signal is sent before the HTTP request is done.
So, what I did is I created a class called RequestCancelation
that uses history
package. It can cancel the requests based on the passed action type, or based on the history.location.pathname
.
RequestCancelation.js
import _ from 'lodash';
import axios from 'axios';
import createHistory from 'history/createBrowserHistory';
// In my case the history is imported from another file, as I pass
// it to the `Router` from `react-router-dom`. For the purpose of this
// example I created the history here.
const history = createHistory();
class RequestCancelation {
static constants = {
cancelationTokens: 'CANCELATION_TOKENS',
}
getTokens() {
return _.get(window, RequestCancelation.constants.cancelationTokens, {});
}
setTokens(tokens) {
return _.set(window, RequestCancelation.constants.cancelationTokens, tokens);
}
deleteTokens(key) {
if (!key) return undefined;
delete window[RequestCancelation.constants.cancelationTokens][key];
return this.getTokens();
}
getLocationKey() {
return _.get(history, 'location.pathname');
}
getCancelToken(type) {
return new axios.CancelToken((c) => {
const cancel = c;
if (typeof window === 'undefined') return;
const tokens = this.getTokens();
if (type) {
tokens[type] = cancel;
} else {
const key = this.getLocationKey();
if (!key) return;
if (!tokens[key]) tokens[key] = [];
tokens[key].push(cancel);
}
this.setTokens(tokens);
});
}
cancelRequest(type) {
if (!type) {
return console.warn('#cancelRequest - please specify \'type\'');
}
if (typeof window === 'undefined') return undefined;
const tokens = this.getTokens();
const cancel = tokens[type];
if (!cancel) return undefined;
cancel();
return this.deleteTokens(type);
}
cancelRequests() {
if (typeof window === 'undefined') return undefined;
const tokens = this.getTokens();
const key = this.getLocationKey();
if (!key) return undefined;
const cancels = tokens[key];
if (!cancels) return undefined;
cancels.forEach(cancel => cancel());
return this.deleteTokens(key);
}
clearTokens() {
if (typeof window === 'undefined') return undefined;
window[RequestCancelation.constants.cancelationTokens] = {};
return this.getTokens();
}
}
const cancelation = new RequestCancelation();
export default cancelation;
Hope this helps someone and maybe someone can improve it :)
Also available as a gist.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With