0

I am using axios and I noticed that say someone goes to Route A and then Quickly clicks on a button to Route B the Route A requests will still be running and weird results can happen.

I am doing something like this

getAllTaxRates: flow(function*() {
  try {

      const response = yield self.rootStore().axios.get('/get');

      applySnapshot(self.taxRates, response.data);

  } catch (error) {
    throw error;
  }
}),

So I am not sure what the best way is to cancel these requests.

chobo2
  • 83,322
  • 195
  • 530
  • 832

2 Answers2

0

I'm encountering the same problem here. Actually not exactly. It's just that my architecture is susceptible to the same kind of problem (now that I'm thinking about it). I guess what we should do, is store the state of the router (perhaps in volatile state) and consult it, before attempting to request any route. Then if necessary we could either stop the route from changing, or cancel the previous request (and requests should be made through a cancelable utility), before making a new one.

Potentially you could also add a middleware that has ability to cancel an async action at any stage. So you could potentially consult the above mentioned flag on every stage of the action and bail out if you detect a conflict. But that won't stop the request itself.

The third option and the best one I guess, would be to create a little volatile state for every request, with a state and a reference to the actual request, so that it could cancel it automatically on destroy. And destroy will be triggered as soon as you assign a new request to the same node on the tree.

jayarjo
  • 16,124
  • 24
  • 94
  • 138
0

You could use a Cancel Token as provided by axios to cancel pending requests at the beginning of a new one (you then have to create a new Token every time I think).

However I remember having a few issues with that method (it froze my whole app) and I ended up using Node-Fetch and the AbortController which works perfectly for my use. (As seen in this answer)

EDIT: Here is roughly how I am doing it,note that my function for the requests is async (using the Context API, but you should be able to do the same with Mobx)

import React from 'react';
import Context from './Context';
import fetch from 'node-fetch';

class Provider extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isLoading: false,
      controller: new AbortController(),
    };
    this.queryAll = this.queryAll.bind(this);
    this.abortAllRequest = this.abortAllRequest.bind(this);
  }

  abortAllRequest() {
    this.state.controller.abort();
    this.setState({
      isLoading: false,
      controller: new AbortController(),
    });
  }

  async queryAll(eans) {
    if (this.state.isLoading) {
      await this.abortAllRequest();
    }

    const signal = this.state.controller.signal;

    const result = await fetch(`/SomUrl/`, {
      method: 'get',
      signal: signal,
    });
  }

  render() {
    return (
      <Context.Provider value={{
        state: this.state,
        queryAll: this.queryAll,
        abortAllRequest: this.abortAllRequest,
      }}>
        {this.props.children}
      </Context.Provider>
    );
  }
}

export default Provider;
Brumor
  • 95
  • 1
  • 9
  • Yea, I saw that but I guess I would have to make this token outside the function and pass it in? as I am not sure where i would store it and would each request need it's own? Like say the page fires 5 requests can I use same token for all 5? – chobo2 Jun 12 '19 at 16:38
  • As said, I'm not sure how it works with Axios. With node-fetch, you can use one AbortController for all requests, but you have to create a new AbortController everytime you call .abort() on the previous one, which you can then store in the state – Brumor Jun 13 '19 at 07:12