@astral/mobx-query 1.18.3 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AuxiliaryQuery/AuxiliaryQuery.d.ts +2 -7
- package/AuxiliaryQuery/AuxiliaryQuery.js +13 -29
- package/InfiniteQuery/InfiniteQuery.d.ts +74 -7
- package/InfiniteQuery/InfiniteQuery.js +93 -30
- package/MobxQuery/MobxQuery.js +8 -4
- package/README.md +248 -1
- package/Sets/InfiniteQuerySet/InfiniteQuerySet.d.ts +1 -1
- package/StatusStorage/StatusStorage.d.ts +4 -0
- package/StatusStorage/StatusStorage.js +18 -0
- package/node/AuxiliaryQuery/AuxiliaryQuery.d.ts +2 -7
- package/node/AuxiliaryQuery/AuxiliaryQuery.js +13 -29
- package/node/InfiniteQuery/InfiniteQuery.d.ts +74 -7
- package/node/InfiniteQuery/InfiniteQuery.js +93 -30
- package/node/MobxQuery/MobxQuery.js +8 -4
- package/node/Sets/InfiniteQuerySet/InfiniteQuerySet.d.ts +1 -1
- package/node/StatusStorage/StatusStorage.d.ts +4 -0
- package/node/StatusStorage/StatusStorage.js +18 -0
- package/package.json +1 -1
|
@@ -4,28 +4,47 @@ exports.InfiniteQuery = exports.DEFAULT_INFINITE_ITEMS_COUNT = void 0;
|
|
|
4
4
|
const mobx_1 = require("mobx");
|
|
5
5
|
const AuxiliaryQuery_1 = require("../AuxiliaryQuery");
|
|
6
6
|
const QueryContainer_1 = require("../QueryContainer");
|
|
7
|
+
const StatusStorage_1 = require("../StatusStorage");
|
|
7
8
|
exports.DEFAULT_INFINITE_ITEMS_COUNT = 30;
|
|
8
9
|
/**
|
|
9
10
|
* Квери для работы с инфинити запросами,
|
|
10
11
|
* которые должны быть закешированы,
|
|
11
12
|
*/
|
|
12
13
|
class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
13
|
-
constructor(executor, { incrementCount = exports.DEFAULT_INFINITE_ITEMS_COUNT, onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage = null, submitValidity, }) {
|
|
14
|
+
constructor(executor, { incrementCount = exports.DEFAULT_INFINITE_ITEMS_COUNT, initialOffset = 0, initialCursor, onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage = null, submitValidity, }) {
|
|
14
15
|
super(statusStorage, backgroundStatusStorage, new AuxiliaryQuery_1.AuxiliaryQuery(statusStorage, backgroundStatusStorage));
|
|
15
16
|
this.executor = executor;
|
|
17
|
+
/**
|
|
18
|
+
* Хранилище статусов для процесса дозапроса данных
|
|
19
|
+
*/
|
|
20
|
+
this.fetchMoreStatusStorage = new StatusStorage_1.StatusStorage();
|
|
21
|
+
/**
|
|
22
|
+
* Стартовый offset для запроса,
|
|
23
|
+
*/
|
|
24
|
+
this.initialOffset = 0;
|
|
25
|
+
/**
|
|
26
|
+
* Стартовое значение курсора, от которого должен произойти первый запрос.
|
|
27
|
+
* Предполагается, что этот элемент должен содержать только id сущности.
|
|
28
|
+
*/
|
|
29
|
+
this.initialCursor = undefined;
|
|
16
30
|
/**
|
|
17
31
|
* Обработчик успешного запроса, проверяет что мы достигли предела
|
|
18
32
|
*/
|
|
19
|
-
this.submitSuccess = (result,
|
|
33
|
+
this.submitSuccess = (result, isLimitReached, isBackward) => {
|
|
20
34
|
var _a;
|
|
21
35
|
this.storage.setData((current) => ({
|
|
22
36
|
offset: current === null || current === void 0 ? void 0 : current.offset,
|
|
23
37
|
data: result,
|
|
24
|
-
isEndReached
|
|
38
|
+
isEndReached: isBackward
|
|
39
|
+
? Boolean(current === null || current === void 0 ? void 0 : current.isEndReached)
|
|
40
|
+
: isLimitReached,
|
|
41
|
+
isStartReached: isBackward
|
|
42
|
+
? isLimitReached
|
|
43
|
+
: Boolean(current === null || current === void 0 ? void 0 : current.isStartReached),
|
|
25
44
|
}));
|
|
26
45
|
(_a = this.submitValidity) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
27
46
|
};
|
|
28
|
-
this.
|
|
47
|
+
this.calcIsLimitReachedByResult = (result) => {
|
|
29
48
|
// убеждаемся что результат запроса действительно массив,
|
|
30
49
|
// и если количество элементов в ответе меньше,
|
|
31
50
|
// чем запрашивалось, значит у бэка их больше нет,
|
|
@@ -50,34 +69,72 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
50
69
|
this.submitSuccess(param, this.isEndReached);
|
|
51
70
|
}
|
|
52
71
|
};
|
|
72
|
+
/**
|
|
73
|
+
* Метод для обогащения параметров текущими значениями для инфинити
|
|
74
|
+
*/
|
|
75
|
+
this.makeInfiniteExecutor = (offset, cursor, direction) => () => this.executor({
|
|
76
|
+
offset,
|
|
77
|
+
count: this.incrementCount,
|
|
78
|
+
cursor,
|
|
79
|
+
direction,
|
|
80
|
+
});
|
|
53
81
|
/**
|
|
54
82
|
* Метод для инвалидации данных
|
|
55
83
|
*/
|
|
56
84
|
this.invalidate = () => {
|
|
57
85
|
this.auxiliary.invalidate();
|
|
58
86
|
};
|
|
87
|
+
this.setOffset = (offset) => {
|
|
88
|
+
this.storage.setData((current) => ({
|
|
89
|
+
offset,
|
|
90
|
+
data: current === null || current === void 0 ? void 0 : current.data,
|
|
91
|
+
isEndReached: Boolean(current === null || current === void 0 ? void 0 : current.isEndReached),
|
|
92
|
+
isStartReached: Boolean(current === null || current === void 0 ? void 0 : current.isStartReached),
|
|
93
|
+
}));
|
|
94
|
+
};
|
|
59
95
|
/**
|
|
60
96
|
* Метод для запроса следующего набора данных
|
|
97
|
+
* @param options - параметры дозапроса данных
|
|
61
98
|
*/
|
|
62
|
-
this.fetchMore = () => {
|
|
99
|
+
this.fetchMore = (options) => {
|
|
100
|
+
var _a, _b;
|
|
101
|
+
const { direction = 'forward', onSuccess } = options !== null && options !== void 0 ? options : {};
|
|
102
|
+
const isBackward = direction === 'backward';
|
|
103
|
+
const isLimitReached = isBackward ? this.isStartReached : this.isEndReached;
|
|
63
104
|
// если мы еще не достигли предела
|
|
64
|
-
if (!
|
|
65
|
-
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
105
|
+
if (!isLimitReached && this.storage.data) {
|
|
106
|
+
this.fetchMoreStatusStorage.setStartLoading();
|
|
107
|
+
const offsetBeforeExecute = this.offset;
|
|
108
|
+
const increment = (isBackward ? -1 : 1) * this.incrementCount;
|
|
109
|
+
const cursor = isBackward
|
|
110
|
+
? (_a = this.data) === null || _a === void 0 ? void 0 : _a[0]
|
|
111
|
+
: (_b = this.data) === null || _b === void 0 ? void 0 : _b[this.data.length - 1];
|
|
112
|
+
// прибавляем к offset число запрашиваемых элементов
|
|
113
|
+
const requestedOffset = Math.max(offsetBeforeExecute + increment, 0);
|
|
114
|
+
this.setOffset(requestedOffset);
|
|
74
115
|
// запускаем запрос с последними параметрами, и флагом необходимости инкремента
|
|
75
116
|
this.auxiliary
|
|
76
|
-
.getUnifiedPromise(this.
|
|
117
|
+
.getUnifiedPromise(this.makeInfiniteExecutor(requestedOffset, cursor, direction), (resData) => {
|
|
118
|
+
var _a;
|
|
119
|
+
this.fetchMoreStatusStorage.setSuccess();
|
|
120
|
+
const currentData = (_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data;
|
|
121
|
+
const result = isBackward
|
|
122
|
+
? [...resData, ...currentData]
|
|
123
|
+
: [...currentData, ...resData];
|
|
124
|
+
this.submitSuccess(result, this.calcIsLimitReachedByResult(resData), isBackward);
|
|
125
|
+
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess({
|
|
126
|
+
offset: requestedOffset,
|
|
127
|
+
cursor: cursor,
|
|
128
|
+
});
|
|
129
|
+
})
|
|
130
|
+
.catch((e) => {
|
|
77
131
|
var _a;
|
|
78
|
-
this.
|
|
132
|
+
this.fetchMoreStatusStorage.setError(e);
|
|
133
|
+
// в случае ошибки, откатываем значение offset к состоянию до дозапроса
|
|
134
|
+
this.setOffset(offsetBeforeExecute);
|
|
135
|
+
return (_a = this.defaultOnError) === null || _a === void 0 ? void 0 : _a.call(this, e);
|
|
79
136
|
})
|
|
80
|
-
.
|
|
137
|
+
.finally(this.fetchMoreStatusStorage.setEndLoading);
|
|
81
138
|
}
|
|
82
139
|
};
|
|
83
140
|
/**
|
|
@@ -99,14 +156,15 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
99
156
|
*/
|
|
100
157
|
this.proceedSync = ({ onSuccess, onError, } = {}) => {
|
|
101
158
|
this.storage.setData((current) => ({
|
|
102
|
-
offset: 0,
|
|
159
|
+
offset: (this.isIdle && this.initialOffset) || 0,
|
|
103
160
|
data: current === null || current === void 0 ? void 0 : current.data,
|
|
104
161
|
isEndReached: false,
|
|
162
|
+
isStartReached: false,
|
|
105
163
|
}));
|
|
106
164
|
this.auxiliary
|
|
107
|
-
.getUnifiedPromise(this.
|
|
165
|
+
.getUnifiedPromise(this.makeInfiniteExecutor(this.offset, this.isIdle ? this.initialCursor : undefined), (resData) => {
|
|
108
166
|
onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess(resData);
|
|
109
|
-
this.submitSuccess(resData, this.
|
|
167
|
+
this.submitSuccess(resData, this.calcIsLimitReachedByResult(resData));
|
|
110
168
|
})
|
|
111
169
|
.catch((e) => {
|
|
112
170
|
var _a;
|
|
@@ -133,12 +191,15 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
133
191
|
return Promise.resolve((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.data);
|
|
134
192
|
}
|
|
135
193
|
this.storage.setData((current) => ({
|
|
136
|
-
offset: 0,
|
|
194
|
+
offset: (this.isIdle && this.initialOffset) || 0,
|
|
137
195
|
data: current === null || current === void 0 ? void 0 : current.data,
|
|
138
196
|
isEndReached: false,
|
|
197
|
+
isStartReached: false,
|
|
139
198
|
}));
|
|
140
|
-
return this.auxiliary.getUnifiedPromise(this.
|
|
199
|
+
return this.auxiliary.getUnifiedPromise(this.makeInfiniteExecutor(this.offset, this.isIdle ? this.initialCursor : undefined), (data) => this.submitSuccess(data, this.calcIsLimitReachedByResult(data)));
|
|
141
200
|
};
|
|
201
|
+
this.initialOffset = initialOffset;
|
|
202
|
+
this.initialCursor = initialCursor;
|
|
142
203
|
this.storage = dataStorage;
|
|
143
204
|
this.incrementCount = incrementCount;
|
|
144
205
|
this.defaultOnError = onError;
|
|
@@ -146,15 +207,16 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
146
207
|
this.defaultFetchPolicy = fetchPolicy;
|
|
147
208
|
this.submitValidity = submitValidity;
|
|
148
209
|
(0, mobx_1.makeObservable)(this, {
|
|
210
|
+
fetchMoreStatus: mobx_1.computed,
|
|
149
211
|
data: mobx_1.computed,
|
|
150
212
|
computedData: mobx_1.computed,
|
|
151
|
-
infiniteExecutor: mobx_1.computed,
|
|
152
213
|
forceUpdate: mobx_1.action,
|
|
153
214
|
async: mobx_1.action,
|
|
154
215
|
sync: mobx_1.action,
|
|
155
216
|
fetchMore: mobx_1.action,
|
|
156
217
|
submitSuccess: mobx_1.action,
|
|
157
218
|
isEndReached: mobx_1.computed,
|
|
219
|
+
isStartReached: mobx_1.computed,
|
|
158
220
|
});
|
|
159
221
|
}
|
|
160
222
|
get isNetworkOnly() {
|
|
@@ -175,13 +237,14 @@ class InfiniteQuery extends QueryContainer_1.QueryContainer {
|
|
|
175
237
|
return Boolean((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.isEndReached);
|
|
176
238
|
}
|
|
177
239
|
/**
|
|
178
|
-
*
|
|
240
|
+
* Флаг того, что мы достигли самого начала запрашиваемых элементов
|
|
179
241
|
*/
|
|
180
|
-
get
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
242
|
+
get isStartReached() {
|
|
243
|
+
var _a;
|
|
244
|
+
return Boolean((_a = this.storage.data) === null || _a === void 0 ? void 0 : _a.isStartReached);
|
|
245
|
+
}
|
|
246
|
+
get fetchMoreStatus() {
|
|
247
|
+
return this.fetchMoreStatusStorage.statuses;
|
|
185
248
|
}
|
|
186
249
|
get computedData() {
|
|
187
250
|
var _a;
|
|
@@ -111,8 +111,9 @@ class MobxQuery {
|
|
|
111
111
|
*/
|
|
112
112
|
this.getCachedQuery = (key, createInstance, type, createParams) => {
|
|
113
113
|
var _a, _b;
|
|
114
|
+
const enabledAutoFetch = (_a = createParams === null || createParams === void 0 ? void 0 : createParams.enabledAutoFetch) !== null && _a !== void 0 ? _a : this.defaultEnabledAutoFetch;
|
|
114
115
|
const fetchPolicy = (createParams === null || createParams === void 0 ? void 0 : createParams.fetchPolicy) || this.defaultFetchPolicy;
|
|
115
|
-
const keys = this.makeKeys(key, fetchPolicy, (
|
|
116
|
+
const keys = this.makeKeys(key, fetchPolicy, (_b = createParams === null || createParams === void 0 ? void 0 : createParams.isBackground) !== null && _b !== void 0 ? _b : false, type, enabledAutoFetch);
|
|
116
117
|
const cachedQuery = this.queriesMap.get(keys.queryKeyHash);
|
|
117
118
|
if (cachedQuery) {
|
|
118
119
|
return cachedQuery;
|
|
@@ -120,7 +121,7 @@ class MobxQuery {
|
|
|
120
121
|
const query = createInstance({
|
|
121
122
|
onError: ((createParams === null || createParams === void 0 ? void 0 : createParams.onError) ||
|
|
122
123
|
this.defaultErrorHandler),
|
|
123
|
-
enabledAutoFetch
|
|
124
|
+
enabledAutoFetch,
|
|
124
125
|
fetchPolicy,
|
|
125
126
|
dataStorage: this.queryDataStorageFactory.getStorage(keys.dataKeyHash),
|
|
126
127
|
statusStorage: this.statusStorageFactory.getStorage(keys.statusKeyHash),
|
|
@@ -137,7 +138,7 @@ class MobxQuery {
|
|
|
137
138
|
/**
|
|
138
139
|
* Метод для создания ключей к внутренним хранилищам
|
|
139
140
|
*/
|
|
140
|
-
this.makeKeys = (rootKey, fetchPolicy, isBackground, type) => {
|
|
141
|
+
this.makeKeys = (rootKey, fetchPolicy, isBackground, type, enabledAutoFetch) => {
|
|
141
142
|
// C введением StrictMode в реакт 18, проявилась проблема,
|
|
142
143
|
// что network-only квери, созданные в одном реакт компоненте,
|
|
143
144
|
// создаются дважды (т.к. все хуки вызываются дважды)
|
|
@@ -151,7 +152,10 @@ class MobxQuery {
|
|
|
151
152
|
// Этот кейс крайне маловероятен, и будет проявляться только в дев режиме с включенным StrictMode,
|
|
152
153
|
// поэтому мы пренебрегаем решением этой проблемы, в угоду упрощения.
|
|
153
154
|
const date = fetchPolicy === 'network-only' ? new Date().setMilliseconds(0) : null;
|
|
154
|
-
const queryKey = [
|
|
155
|
+
const queryKey = [
|
|
156
|
+
...rootKey,
|
|
157
|
+
{ fetchPolicy, date, isBackground, type, enabledAutoFetch },
|
|
158
|
+
];
|
|
155
159
|
const queryKeyHash = this.serialize(queryKey);
|
|
156
160
|
const dataKeyHash = this.serialize([...rootKey, { type }]);
|
|
157
161
|
const statusKeyHash = this.serialize([...rootKey, { type, date }]);
|
|
@@ -7,7 +7,7 @@ export type InfiniteQuerySetConfig = {
|
|
|
7
7
|
};
|
|
8
8
|
export type InfiniteQuerySetConfigurator<TParams extends any[], TResponse> = (...params: TParams) => {
|
|
9
9
|
keys?: CacheKey[];
|
|
10
|
-
execute: (infiniteParams: InfiniteParams) => Promise<TResponse[]>;
|
|
10
|
+
execute: (infiniteParams: InfiniteParams<TResponse>) => Promise<TResponse[]>;
|
|
11
11
|
};
|
|
12
12
|
/**
|
|
13
13
|
* Набор infinite queries под одним ключем с целью повышения читаемости кода и удобства инвалидации по общему ключу
|
|
@@ -24,6 +24,10 @@ export declare class StatusStorage<TError> implements Statuses<TError> {
|
|
|
24
24
|
*/
|
|
25
25
|
isSuccess: boolean;
|
|
26
26
|
setStatues: (data: Statuses<TError>) => void;
|
|
27
|
+
setStartLoading: () => void;
|
|
28
|
+
setError: (error: TError) => void;
|
|
29
|
+
setSuccess: () => void;
|
|
30
|
+
setEndLoading: () => void;
|
|
27
31
|
get statuses(): Statuses<TError>;
|
|
28
32
|
}
|
|
29
33
|
export declare class StatusStorageFactory extends StorageFactory<StatusStorage<any>> {
|
|
@@ -27,6 +27,24 @@ class StatusStorage {
|
|
|
27
27
|
this.error = data.error;
|
|
28
28
|
this.isSuccess = data.isSuccess;
|
|
29
29
|
};
|
|
30
|
+
this.setStartLoading = () => {
|
|
31
|
+
this.isLoading = true;
|
|
32
|
+
this.isError = false;
|
|
33
|
+
this.isSuccess = false;
|
|
34
|
+
};
|
|
35
|
+
this.setError = (error) => {
|
|
36
|
+
this.isSuccess = false;
|
|
37
|
+
this.isError = true;
|
|
38
|
+
this.error = error;
|
|
39
|
+
};
|
|
40
|
+
this.setSuccess = () => {
|
|
41
|
+
this.error = undefined;
|
|
42
|
+
this.isError = false;
|
|
43
|
+
this.isSuccess = true;
|
|
44
|
+
};
|
|
45
|
+
this.setEndLoading = () => {
|
|
46
|
+
this.isLoading = false;
|
|
47
|
+
};
|
|
30
48
|
(0, mobx_1.makeAutoObservable)(this);
|
|
31
49
|
}
|
|
32
50
|
get statuses() {
|