Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -524,3 +524,10 @@ animalStore.fetch().then(() => {
console.log(animalStore.at(0).name); // Garfield
});
```

You can also cancel the previous request by passing `{ cancelPreviousFetch: true }` to fetch

```js
animalStore.fetch(); // request cancelled
animalStore.fetch({ cancelPreviousFetch: true });
```
28 changes: 22 additions & 6 deletions dist/mobx-spine.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,11 +234,16 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con

return desc;
}

var AVAILABLE_CONST_OPTIONS = ['relations', 'limit', 'comparator', 'params', 'repository'];

var Store = (_class = (_temp = _class2 = function () {
createClass(Store, [{
key: 'url',

// The set of models has changed

// Holds the fetch parameters
value: function url() {
// Try to auto-generate the URL.
var bname = this.constructor.backendResourceName;
Expand All @@ -247,10 +252,6 @@ var Store = (_class = (_temp = _class2 = function () {
}
return null;
}
// The set of models has changed

// Holds the fetch parameters

}, {
key: 'initialize',

Expand Down Expand Up @@ -296,6 +297,7 @@ var Store = (_class = (_temp = _class2 = function () {
lodash.forIn(options, function (value, option) {
invariant(AVAILABLE_CONST_OPTIONS.includes(option), 'Unknown option passed to store: ' + option);
});
this.abortController = new AbortController();
this.__repository = options.repository;
if (options.relations) {
this.__parseRelations(options.relations);
Expand Down Expand Up @@ -481,6 +483,12 @@ var Store = (_class = (_temp = _class2 = function () {

var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

if (options.cancelPreviousFetch) {
this.abortController.abort();
this.abortController = new AbortController();
this.__pendingRequestCount = 0;
}
options.abortSignal = this.abortController.signal;

var data = this.buildFetchData(options);
var promise = this.wrapPendingRequestCount(this.__getApi().fetchStore({
Expand All @@ -492,7 +500,15 @@ var Store = (_class = (_temp = _class2 = function () {
_this5.fromBackend(res);

return res.response;
})));
})).catch(function (e) {
if (Axios.isCancel(e)) {
// correct __pendingRequestCount
_this5.__pendingRequestCount++;
return null;
} else {
throw e;
}
}));

return promise;
}
Expand Down Expand Up @@ -2257,7 +2273,7 @@ function checkLuxonDateTime(attr, value) {
}

var LUXON_DATE_FORMAT = 'yyyy-LL-dd';
var LUXON_DATETIME_FORMAT = "yyyy'-'LL'-'dd'T'HH':'mm':'ssZZ";
var LUXON_DATETIME_FORMAT = 'yyyy\'-\'LL\'-\'dd\'T\'HH\':\'mm\':\'ssZZ';

var CASTS = {
momentDate: {
Expand Down
28 changes: 22 additions & 6 deletions dist/mobx-spine.es.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,11 +228,16 @@ function _applyDecoratedDescriptor(target, property, decorators, descriptor, con

return desc;
}

var AVAILABLE_CONST_OPTIONS = ['relations', 'limit', 'comparator', 'params', 'repository'];

var Store = (_class = (_temp = _class2 = function () {
createClass(Store, [{
key: 'url',

// The set of models has changed

// Holds the fetch parameters
value: function url() {
// Try to auto-generate the URL.
var bname = this.constructor.backendResourceName;
Expand All @@ -241,10 +246,6 @@ var Store = (_class = (_temp = _class2 = function () {
}
return null;
}
// The set of models has changed

// Holds the fetch parameters

}, {
key: 'initialize',

Expand Down Expand Up @@ -290,6 +291,7 @@ var Store = (_class = (_temp = _class2 = function () {
forIn(options, function (value, option) {
invariant(AVAILABLE_CONST_OPTIONS.includes(option), 'Unknown option passed to store: ' + option);
});
this.abortController = new AbortController();
this.__repository = options.repository;
if (options.relations) {
this.__parseRelations(options.relations);
Expand Down Expand Up @@ -475,6 +477,12 @@ var Store = (_class = (_temp = _class2 = function () {

var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};

if (options.cancelPreviousFetch) {
this.abortController.abort();
this.abortController = new AbortController();
this.__pendingRequestCount = 0;
}
options.abortSignal = this.abortController.signal;

var data = this.buildFetchData(options);
var promise = this.wrapPendingRequestCount(this.__getApi().fetchStore({
Expand All @@ -486,7 +494,15 @@ var Store = (_class = (_temp = _class2 = function () {
_this5.fromBackend(res);

return res.response;
})));
})).catch(function (e) {
if (Axios.isCancel(e)) {
// correct __pendingRequestCount
_this5.__pendingRequestCount++;
return null;
} else {
throw e;
}
}));

return promise;
}
Expand Down Expand Up @@ -2251,7 +2267,7 @@ function checkLuxonDateTime(attr, value) {
}

var LUXON_DATE_FORMAT = 'yyyy-LL-dd';
var LUXON_DATETIME_FORMAT = "yyyy'-'LL'-'dd'T'HH':'mm':'ssZZ";
var LUXON_DATETIME_FORMAT = 'yyyy\'-\'LL\'-\'dd\'T\'HH\':\'mm\':\'ssZZ';

var CASTS = {
momentDate: {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mobx-spine",
"version": "0.28.5",
"version": "0.28.6",
"license": "ISC",
"author": "Kees Kluskens <kees@webduck.nl>",
"description": "MobX with support for models, relations and an API.",
Expand Down
19 changes: 19 additions & 0 deletions src/Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
uniqBy,
} from 'lodash';
import { invariant } from './utils';
import Axios from 'axios';

const AVAILABLE_CONST_OPTIONS = [
'relations',
'limit',
Expand All @@ -37,6 +39,7 @@ export default class Store {
__activeRelations = [];
Model = null;
api = null;
abortController;
__repository;
static backendResourceName = '';

Expand Down Expand Up @@ -80,6 +83,7 @@ export default class Store {
`Unknown option passed to store: ${option}`
);
});
this.abortController = new AbortController();
this.__repository = options.repository;
if (options.relations) {
this.__parseRelations(options.relations);
Expand Down Expand Up @@ -264,6 +268,12 @@ export default class Store {

@action
fetch(options = {}) {
if (options.cancelPreviousFetch) {
this.abortController.abort();
this.abortController = new AbortController();
this.__pendingRequestCount = 0;
}
options.abortSignal = this.abortController.signal;

const data = this.buildFetchData(options);
const promise = this.wrapPendingRequestCount(
Expand All @@ -279,6 +289,15 @@ export default class Store {

return res.response;
}))
.catch(e => {
if (Axios.isCancel(e)) {
// correct __pendingRequestCount
this.__pendingRequestCount++
return null;
} else {
throw e;
}
})
);

return promise;
Expand Down
76 changes: 76 additions & 0 deletions src/__tests__/Store.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import customersWithTownRestaurantsUnbalanced from './fixtures/customers-with-to
import townsWithRestaurantsAndCustomersNoIdList from './fixtures/towns-with-restaurants-and-customers-no-id-list.json';
import customersWithOldTowns from './fixtures/customers-with-old-towns.json';
import animalsData from './fixtures/animals.json';
import animalsDataIdOne from './fixtures/animals-id-1.json';
import pagination0Data from './fixtures/pagination/0.json';
import pagination1Data from './fixtures/pagination/1.json';
import pagination2Data from './fixtures/pagination/2.json';
Expand Down Expand Up @@ -740,6 +741,81 @@ describe('requests', () => {
expect(animalStore.isLoading).toBe(false);
});
});

test('fetch cancelPreviousFetch should update pendingRequestCount', async () => {
const animalStore = new AnimalStore();

mock.onAny().reply(config => {
return new Promise((resolve, reject) => {
if (config.signal.aborted) {
setTimeout(() => {
// reject after 1 second
reject({ __CANCEL__: true })
}, 1000)
}
setTimeout(() => {
resolve([200, animalsDataIdOne])
}, 100)
})
});
const p1 = animalStore.fetch()
const p2 = animalStore.fetch({ cancelPreviousFetch: true, params: { id: 1 } })

// we ignore previous request, so number of pending = 1
expect(animalStore.__pendingRequestCount).toBe(1);
expect(animalStore.isLoading).toBe(true);
expect(animalStore.models.length).toBe(0);

// p2 is first to resolve, should mark store as "done"
await p2
expect(animalStore.__pendingRequestCount).toBe(0);
expect(animalStore.isLoading).toBe(false);
expect(animalStore.models.length).toBe(1);

// p1 is cancelled, should not change stuff
await p1
expect(animalStore.__pendingRequestCount).toBe(0);
expect(animalStore.isLoading).toBe(false);
expect(animalStore.models.length).toBe(1);
});

test('fetch cancelPreviousFetch when received in order, but canceled', async () => {
const animalStore = new AnimalStore();

mock.onAny().reply(config => {
return new Promise((resolve, reject) => {
if (config.signal.aborted) {
setTimeout(() => {
// reject after 100 ms
reject({ __CANCEL__: true })
}, 100)
}
setTimeout(() => {
// resolve other request later
resolve([200, animalsDataIdOne])
}, 1000)
})
});
const p1 = animalStore.fetch()
const p2 = animalStore.fetch({ cancelPreviousFetch: true, params: { id: 1 } })

// we ignore previous request, so number of pending = 1
expect(animalStore.__pendingRequestCount).toBe(1);
expect(animalStore.isLoading).toBe(true);
expect(animalStore.models.length).toBe(0);

// p1 is first to resolve, should be cancelled
await p1
expect(animalStore.__pendingRequestCount).toBe(1);
expect(animalStore.isLoading).toBe(true);
expect(animalStore.models.length).toBe(0);

// p2 is resolved, data should udpate
await p2
expect(animalStore.__pendingRequestCount).toBe(0);
expect(animalStore.isLoading).toBe(false);
expect(animalStore.models.length).toBe(1);
});
});

describe('Pagination', () => {
Expand Down
11 changes: 11 additions & 0 deletions src/__tests__/fixtures/animals-id-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"data": [
{
"id": 1,
"name": "Madagascar"
}
],
"meta": {
"total_records": 1
}
}
Loading