最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React - requests cancellation on page change - Stack Overflow

programmeradmin1浏览0评论

I'm using axios for fetching/ posting the data to my server and my current approach looks like this:

1. Request are made using redux actions with 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 }),
  });
};

2. 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.

3. Cancelling on ponentWillUnmount

import * as Types from './path/to/action/types';

const cancelToken = (type) => {
  const cancel = _.get(window, `cancelationTokens.${type}`);
  if (!cancel) return;
  cancel();
}

ponentWillUnmount() {
  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 ponentWillUnmount.

My questions:

  1. Is there a way to automatically cancel the ongoing request if user changes the page in my app?
  2. If yes - is it the correct way to do this, or are there easier way to cancel requests?

I'm using axios for fetching/ posting the data to my server and my current approach looks like this:

1. Request are made using redux actions with 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 }),
  });
};

2. 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.

3. Cancelling on ponentWillUnmount

import * as Types from './path/to/action/types';

const cancelToken = (type) => {
  const cancel = _.get(window, `cancelationTokens.${type}`);
  if (!cancel) return;
  cancel();
}

ponentWillUnmount() {
  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 ponentWillUnmount.

My questions:

  1. Is there a way to automatically cancel the ongoing request if user changes the page in my app?
  2. If yes - is it the correct way to do this, or are there easier way to cancel requests?
Share Improve this question edited Oct 29, 2021 at 12:07 Ahmet Emre Kilinc 6,99319 gold badges36 silver badges47 bronze badges asked Oct 2, 2018 at 11:59 mdmbmdmb 5,3039 gold badges51 silver badges97 bronze badges
Add a ment  | 

1 Answer 1

Reset to default 4

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.

发布评论

评论列表(0)

  1. 暂无评论