@astral/mobx-query 1.4.0 → 1.5.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 +25 -26
- package/AuxiliaryQuery/AuxiliaryQuery.js +59 -34
- package/DataStorage/DataStorage.d.ts +10 -12
- package/DataStorage/DataStorage.js +11 -17
- package/InfiniteQuery/InfiniteQuery.d.ts +37 -55
- package/InfiniteQuery/InfiniteQuery.js +31 -57
- package/MobxQuery/MobxQuery.d.ts +35 -20
- package/MobxQuery/MobxQuery.js +22 -14
- package/Mutation/Mutation.d.ts +11 -34
- package/Mutation/Mutation.js +11 -38
- package/Query/Query.d.ts +32 -50
- package/Query/Query.js +25 -53
- package/QueryContainer/QueryContainer.d.ts +61 -0
- package/QueryContainer/QueryContainer.js +79 -0
- package/QueryContainer/index.d.ts +1 -0
- package/QueryContainer/index.js +1 -0
- package/README.md +25 -0
- package/StatusStorage/StatusStorage.d.ts +25 -0
- package/StatusStorage/StatusStorage.js +36 -0
- package/StatusStorage/index.d.ts +1 -0
- package/StatusStorage/index.js +1 -0
- package/StorageFactory/StorageFactory.d.ts +13 -0
- package/StorageFactory/StorageFactory.js +19 -0
- package/StorageFactory/index.d.ts +1 -0
- package/StorageFactory/index.js +1 -0
- package/package.json +1 -1
- package/types.d.ts +12 -12
package/Query/Query.d.ts
CHANGED
|
@@ -1,108 +1,90 @@
|
|
|
1
|
+
import { AuxiliaryQuery } from '../AuxiliaryQuery';
|
|
1
2
|
import type { FetchPolicy, QueryBaseActions, Sync, SyncParams } from '../types';
|
|
2
3
|
import type { DataStorage } from '../DataStorage';
|
|
4
|
+
import { QueryContainer } from '../QueryContainer';
|
|
5
|
+
import { type StatusStorage } from '../StatusStorage';
|
|
3
6
|
/**
|
|
4
|
-
*
|
|
7
|
+
* исполнитель запроса
|
|
5
8
|
*/
|
|
6
9
|
export type QueryExecutor<TResult> = () => Promise<TResult>;
|
|
7
|
-
export type QueryParams<TResult, TError> = {
|
|
10
|
+
export type QueryParams<TResult, TError, TIsBackground extends boolean = false> = {
|
|
8
11
|
/**
|
|
9
|
-
*
|
|
12
|
+
* обработчик ошибки, вызываемый по умолчанию
|
|
10
13
|
*/
|
|
11
14
|
onError?: SyncParams<TResult, TError>['onError'];
|
|
12
15
|
/**
|
|
13
|
-
*
|
|
16
|
+
* флаг, отвечающий за автоматический запрос данных при обращении к полю data
|
|
14
17
|
*/
|
|
15
18
|
enabledAutoFetch?: boolean;
|
|
16
19
|
fetchPolicy?: FetchPolicy;
|
|
17
20
|
/**
|
|
18
|
-
*
|
|
21
|
+
* инстанс хранилища данных
|
|
19
22
|
*/
|
|
20
23
|
dataStorage: DataStorage<TResult>;
|
|
24
|
+
/**
|
|
25
|
+
* инстанс хранилища основных статусов
|
|
26
|
+
*/
|
|
27
|
+
statusStorage: StatusStorage<TError>;
|
|
28
|
+
/**
|
|
29
|
+
* инстанс хранилища фоновых статусов
|
|
30
|
+
*/
|
|
31
|
+
backgroundStatusStorage?: TIsBackground extends true ? StatusStorage<TError> : null | undefined;
|
|
21
32
|
};
|
|
22
33
|
/**
|
|
23
|
-
*
|
|
34
|
+
* стор для работы с запросами,
|
|
24
35
|
* которые должны быть закешированы,
|
|
25
36
|
* но им не требуется усложнение в виде работы с фильтрами и инфинити запросами
|
|
26
37
|
*/
|
|
27
|
-
export declare class Query<TResult, TError = void> implements QueryBaseActions<TResult, TError, undefined> {
|
|
38
|
+
export declare class Query<TResult, TError = void, TIsBackground extends boolean = false> extends QueryContainer<TError, AuxiliaryQuery<TResult, TError>, TIsBackground> implements QueryBaseActions<TResult, TError, undefined> {
|
|
39
|
+
private readonly executor;
|
|
28
40
|
/**
|
|
29
|
-
*
|
|
30
|
-
*/
|
|
31
|
-
private auxiliary;
|
|
32
|
-
/**
|
|
33
|
-
* @description флаг, по которому реактивно определяется необходимость запуска инвалидации
|
|
34
|
-
*/
|
|
35
|
-
private isInvalid;
|
|
36
|
-
/**
|
|
37
|
-
* @description хранилище данных, для обеспечения возможности синхронизации данных между разными инстансами
|
|
41
|
+
* хранилище данных, для обеспечения возможности синхронизации данных между разными инстансами
|
|
38
42
|
*/
|
|
39
43
|
private storage;
|
|
40
44
|
/**
|
|
41
|
-
*
|
|
42
|
-
* что будет использоваться что-то из слоя sources
|
|
43
|
-
*/
|
|
44
|
-
private executor;
|
|
45
|
-
/**
|
|
46
|
-
* @description обработчик ошибки, вызываемый по умолчанию
|
|
45
|
+
* обработчик ошибки, вызываемый по умолчанию
|
|
47
46
|
*/
|
|
48
47
|
private defaultOnError?;
|
|
49
48
|
/**
|
|
50
|
-
*
|
|
49
|
+
* флаг, отвечающий за автоматический запрос данных при обращении к полю data
|
|
51
50
|
*/
|
|
52
51
|
private enabledAutoFetch?;
|
|
53
52
|
/**
|
|
54
|
-
*
|
|
53
|
+
* стандартное поведение политики кеширования
|
|
55
54
|
*/
|
|
56
55
|
private readonly defaultFetchPolicy?;
|
|
57
|
-
constructor(executor: QueryExecutor<TResult>, { onError, enabledAutoFetch, fetchPolicy, dataStorage, }: QueryParams<TResult, TError>);
|
|
56
|
+
constructor(executor: QueryExecutor<TResult>, { onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage, }: QueryParams<TResult, TError, TIsBackground>);
|
|
58
57
|
private get isNetworkOnly();
|
|
59
58
|
/**
|
|
60
|
-
*
|
|
59
|
+
* метод для инвалидации данных
|
|
61
60
|
*/
|
|
62
61
|
invalidate: () => void;
|
|
63
62
|
/**
|
|
64
|
-
*
|
|
63
|
+
* синхронный метод получения данных
|
|
65
64
|
*/
|
|
66
65
|
sync: Sync<TResult, TError, undefined>;
|
|
67
66
|
/**
|
|
68
|
-
*
|
|
67
|
+
* обработчик успешного ответа
|
|
69
68
|
*/
|
|
70
69
|
private submitSuccess;
|
|
71
70
|
/**
|
|
72
|
-
*
|
|
71
|
+
* форс метод для установки данных
|
|
73
72
|
*/
|
|
74
73
|
forceUpdate: (data: TResult) => void;
|
|
75
74
|
/**
|
|
76
|
-
*
|
|
75
|
+
* метод для переиспользования синхронной логики запроса
|
|
77
76
|
*/
|
|
78
|
-
|
|
77
|
+
protected proceedSync: Sync<TResult, TError>;
|
|
79
78
|
/**
|
|
80
|
-
*
|
|
79
|
+
* асинхронный метод получения данных,
|
|
81
80
|
* предполагается, что нужно будет самостоятельно обрабатывать ошибку
|
|
82
81
|
*/
|
|
83
82
|
async: () => Promise<TResult>;
|
|
84
83
|
/**
|
|
85
|
-
*
|
|
84
|
+
* содержит реактивные данные
|
|
86
85
|
* благодаря mobx, при изменении isInvalid, свойство будет вычисляться заново,
|
|
87
86
|
* следовательно, стриггерится условие невалидности,
|
|
88
87
|
* и начнется запрос, в результате которого, данные обновятся
|
|
89
88
|
*/
|
|
90
89
|
get data(): TResult | undefined;
|
|
91
|
-
/**
|
|
92
|
-
* @description флаг загрузки данных
|
|
93
|
-
*/
|
|
94
|
-
get isLoading(): boolean;
|
|
95
|
-
/**
|
|
96
|
-
* @description флаг обозначающий, что последний запрос был зафейлен
|
|
97
|
-
*/
|
|
98
|
-
get isError(): boolean;
|
|
99
|
-
/**
|
|
100
|
-
* @description данные о последней ошибке
|
|
101
|
-
*/
|
|
102
|
-
get error(): TError | undefined;
|
|
103
|
-
/**
|
|
104
|
-
* @description флаг обозначающий, что последний запрос был успешно завершен
|
|
105
|
-
*/
|
|
106
|
-
get isSuccess(): boolean;
|
|
107
|
-
get isIdle(): boolean;
|
|
108
90
|
}
|
package/Query/Query.js
CHANGED
|
@@ -1,52 +1,46 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { action, computed, makeObservable, when } from 'mobx';
|
|
2
2
|
import { AuxiliaryQuery } from '../AuxiliaryQuery';
|
|
3
|
+
import { QueryContainer } from '../QueryContainer';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
+
* стор для работы с запросами,
|
|
5
6
|
* которые должны быть закешированы,
|
|
6
7
|
* но им не требуется усложнение в виде работы с фильтрами и инфинити запросами
|
|
7
8
|
*/
|
|
8
|
-
export class Query {
|
|
9
|
-
constructor(executor, { onError, enabledAutoFetch, fetchPolicy, dataStorage, }) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*/
|
|
13
|
-
this.auxiliary = new AuxiliaryQuery();
|
|
14
|
-
/**
|
|
15
|
-
* @description флаг, по которому реактивно определяется необходимость запуска инвалидации
|
|
16
|
-
*/
|
|
17
|
-
this.isInvalid = false;
|
|
9
|
+
export class Query extends QueryContainer {
|
|
10
|
+
constructor(executor, { onError, enabledAutoFetch, fetchPolicy, dataStorage, statusStorage, backgroundStatusStorage = null, }) {
|
|
11
|
+
super(statusStorage, backgroundStatusStorage, new AuxiliaryQuery(statusStorage, backgroundStatusStorage));
|
|
12
|
+
this.executor = executor;
|
|
18
13
|
/**
|
|
19
|
-
*
|
|
14
|
+
* метод для инвалидации данных
|
|
20
15
|
*/
|
|
21
16
|
this.invalidate = () => {
|
|
22
|
-
this.
|
|
17
|
+
this.auxiliary.invalidate();
|
|
23
18
|
};
|
|
24
19
|
/**
|
|
25
|
-
*
|
|
20
|
+
* синхронный метод получения данных
|
|
26
21
|
*/
|
|
27
22
|
this.sync = (params) => {
|
|
28
23
|
const isInstanceAllow = !(this.isLoading || this.isSuccess);
|
|
29
|
-
if (this.isNetworkOnly || this.isInvalid || isInstanceAllow) {
|
|
24
|
+
if (this.isNetworkOnly || this.auxiliary.isInvalid || isInstanceAllow) {
|
|
30
25
|
this.proceedSync(params);
|
|
31
26
|
}
|
|
32
27
|
};
|
|
33
28
|
/**
|
|
34
|
-
*
|
|
29
|
+
* обработчик успешного ответа
|
|
35
30
|
*/
|
|
36
31
|
this.submitSuccess = (resData) => {
|
|
37
32
|
this.storage.setData(resData);
|
|
38
|
-
this.isInvalid = false;
|
|
39
33
|
return resData;
|
|
40
34
|
};
|
|
41
35
|
/**
|
|
42
|
-
*
|
|
36
|
+
* форс метод для установки данных
|
|
43
37
|
*/
|
|
44
38
|
this.forceUpdate = (data) => {
|
|
45
39
|
this.auxiliary.submitSuccess();
|
|
46
40
|
this.submitSuccess(data);
|
|
47
41
|
};
|
|
48
42
|
/**
|
|
49
|
-
*
|
|
43
|
+
* метод для переиспользования синхронной логики запроса
|
|
50
44
|
*/
|
|
51
45
|
this.proceedSync = (options) => {
|
|
52
46
|
const { onSuccess, onError } = options || {};
|
|
@@ -67,29 +61,34 @@ export class Query {
|
|
|
67
61
|
});
|
|
68
62
|
};
|
|
69
63
|
/**
|
|
70
|
-
*
|
|
64
|
+
* асинхронный метод получения данных,
|
|
71
65
|
* предполагается, что нужно будет самостоятельно обрабатывать ошибку
|
|
72
66
|
*/
|
|
73
67
|
this.async = () => {
|
|
74
|
-
if (!this.isNetworkOnly && this.isSuccess && !this.isInvalid) {
|
|
68
|
+
if (!this.isNetworkOnly && this.isSuccess && !this.auxiliary.isInvalid) {
|
|
75
69
|
return Promise.resolve(this.storage.data);
|
|
76
70
|
}
|
|
77
71
|
return this.auxiliary
|
|
78
72
|
.getUnifiedPromise(this.executor)
|
|
79
73
|
.then(this.submitSuccess);
|
|
80
74
|
};
|
|
81
|
-
this.executor = executor;
|
|
82
75
|
this.defaultOnError = onError;
|
|
83
76
|
this.enabledAutoFetch = enabledAutoFetch;
|
|
84
77
|
this.defaultFetchPolicy = fetchPolicy;
|
|
85
78
|
this.storage = dataStorage;
|
|
86
|
-
|
|
79
|
+
makeObservable(this, {
|
|
80
|
+
async: action,
|
|
81
|
+
sync: action,
|
|
82
|
+
forceUpdate: action,
|
|
83
|
+
data: computed,
|
|
84
|
+
submitSuccess: action,
|
|
85
|
+
});
|
|
87
86
|
}
|
|
88
87
|
get isNetworkOnly() {
|
|
89
88
|
return this.defaultFetchPolicy === 'network-only';
|
|
90
89
|
}
|
|
91
90
|
/**
|
|
92
|
-
*
|
|
91
|
+
* содержит реактивные данные
|
|
93
92
|
* благодаря mobx, при изменении isInvalid, свойство будет вычисляться заново,
|
|
94
93
|
* следовательно, стриггерится условие невалидности,
|
|
95
94
|
* и начнется запрос, в результате которого, данные обновятся
|
|
@@ -99,7 +98,7 @@ export class Query {
|
|
|
99
98
|
!this.isSuccess &&
|
|
100
99
|
!this.isLoading &&
|
|
101
100
|
!this.isError;
|
|
102
|
-
if (this.isInvalid || shouldSync) {
|
|
101
|
+
if (this.auxiliary.isInvalid || shouldSync) {
|
|
103
102
|
// т.к. при вызове апдейта, изменяются флаги, на которые подписан data,
|
|
104
103
|
// нужно вызывать этот экшн асинхронно
|
|
105
104
|
when(() => true, this.proceedSync);
|
|
@@ -107,31 +106,4 @@ export class Query {
|
|
|
107
106
|
// возвращаем имеющиеся данные
|
|
108
107
|
return this.storage.data;
|
|
109
108
|
}
|
|
110
|
-
/**
|
|
111
|
-
* @description флаг загрузки данных
|
|
112
|
-
*/
|
|
113
|
-
get isLoading() {
|
|
114
|
-
return this.auxiliary.isLoading;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* @description флаг обозначающий, что последний запрос был зафейлен
|
|
118
|
-
*/
|
|
119
|
-
get isError() {
|
|
120
|
-
return this.auxiliary.isError;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* @description данные о последней ошибке
|
|
124
|
-
*/
|
|
125
|
-
get error() {
|
|
126
|
-
return this.auxiliary.error;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* @description флаг обозначающий, что последний запрос был успешно завершен
|
|
130
|
-
*/
|
|
131
|
-
get isSuccess() {
|
|
132
|
-
return this.auxiliary.isSuccess;
|
|
133
|
-
}
|
|
134
|
-
get isIdle() {
|
|
135
|
-
return this.auxiliary.isIdle;
|
|
136
|
-
}
|
|
137
109
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type StatusStorage } from '../StatusStorage';
|
|
2
|
+
export type QueryContainerAuxiliary = {
|
|
3
|
+
isIdle: boolean;
|
|
4
|
+
};
|
|
5
|
+
type Statuses<TError> = StatusStorage<TError>;
|
|
6
|
+
/**
|
|
7
|
+
* Контейнер для бойлерплейт части,
|
|
8
|
+
* позволяет не повторять в каждом наследуемом классе использование стандартных статусов
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class QueryContainer<TError, TAuxiliary extends QueryContainerAuxiliary, TIsBackground extends boolean> implements Statuses<TError> {
|
|
11
|
+
private readonly statusStorage;
|
|
12
|
+
private readonly backgroundStatusStorage;
|
|
13
|
+
protected readonly auxiliary: TAuxiliary;
|
|
14
|
+
protected constructor(statusStorage: StatusStorage<TError>, backgroundStatusStorage: StatusStorage<TError> | null, auxiliary: TAuxiliary);
|
|
15
|
+
/**
|
|
16
|
+
* флаг загрузки данных
|
|
17
|
+
*/
|
|
18
|
+
get isLoading(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* флаг обозначающий, что последний запрос был зафейлен
|
|
21
|
+
*/
|
|
22
|
+
get isError(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* данные о последней ошибке
|
|
25
|
+
*/
|
|
26
|
+
get error(): TError | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* флаг обозначающий, что последний запрос был успешно завершен
|
|
29
|
+
*/
|
|
30
|
+
get isSuccess(): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* флаг, обозначающий простаивание, т.е. запроса еще не было
|
|
33
|
+
*/
|
|
34
|
+
get isIdle(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* статусы, изменяющиеся после первого успешного запроса в режиме isBackground: true
|
|
37
|
+
* @example
|
|
38
|
+
* const query = mobxQuery.createQuery(
|
|
39
|
+
* ['some cache key'],
|
|
40
|
+
* () => Promise.resolve('foo'),
|
|
41
|
+
* { isBackground: true }
|
|
42
|
+
* );
|
|
43
|
+
*
|
|
44
|
+
* await query.async();
|
|
45
|
+
* console.log(query.isLoading); // переключался в true на момент запроса
|
|
46
|
+
* console.log(query.isSuccess); // true
|
|
47
|
+
*
|
|
48
|
+
* query.invalidate();
|
|
49
|
+
* await query.async();
|
|
50
|
+
* console.log(query.isLoading); // не изменялся
|
|
51
|
+
* console.log(query.isSuccess); // остался неизменным - true
|
|
52
|
+
*
|
|
53
|
+
* console.log(query.backgroundStatus.isLoading); // переключался в true на момент обновления
|
|
54
|
+
* console.log(query.backgroundStatus.isSuccess); // true
|
|
55
|
+
*
|
|
56
|
+
* @exception isBackground:false (а так же по умолчанию) background не доступен, и равен null
|
|
57
|
+
* @exception Mutation не доступен в мутации и равен null
|
|
58
|
+
*/
|
|
59
|
+
get background(): TIsBackground extends true ? Statuses<TError> : null;
|
|
60
|
+
}
|
|
61
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { computed, makeObservable } from 'mobx';
|
|
2
|
+
/**
|
|
3
|
+
* Контейнер для бойлерплейт части,
|
|
4
|
+
* позволяет не повторять в каждом наследуемом классе использование стандартных статусов
|
|
5
|
+
*/
|
|
6
|
+
export class QueryContainer {
|
|
7
|
+
constructor(statusStorage, backgroundStatusStorage, auxiliary) {
|
|
8
|
+
this.statusStorage = statusStorage;
|
|
9
|
+
this.backgroundStatusStorage = backgroundStatusStorage;
|
|
10
|
+
this.auxiliary = auxiliary;
|
|
11
|
+
makeObservable(this, {
|
|
12
|
+
error: computed,
|
|
13
|
+
isError: computed,
|
|
14
|
+
isIdle: computed,
|
|
15
|
+
isLoading: computed,
|
|
16
|
+
isSuccess: computed,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* флаг загрузки данных
|
|
21
|
+
*/
|
|
22
|
+
get isLoading() {
|
|
23
|
+
return this.statusStorage.isLoading;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* флаг обозначающий, что последний запрос был зафейлен
|
|
27
|
+
*/
|
|
28
|
+
get isError() {
|
|
29
|
+
return this.statusStorage.isError;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* данные о последней ошибке
|
|
33
|
+
*/
|
|
34
|
+
get error() {
|
|
35
|
+
return this.statusStorage.error;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* флаг обозначающий, что последний запрос был успешно завершен
|
|
39
|
+
*/
|
|
40
|
+
get isSuccess() {
|
|
41
|
+
return this.statusStorage.isSuccess;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* флаг, обозначающий простаивание, т.е. запроса еще не было
|
|
45
|
+
*/
|
|
46
|
+
get isIdle() {
|
|
47
|
+
return this.auxiliary.isIdle;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* статусы, изменяющиеся после первого успешного запроса в режиме isBackground: true
|
|
51
|
+
* @example
|
|
52
|
+
* const query = mobxQuery.createQuery(
|
|
53
|
+
* ['some cache key'],
|
|
54
|
+
* () => Promise.resolve('foo'),
|
|
55
|
+
* { isBackground: true }
|
|
56
|
+
* );
|
|
57
|
+
*
|
|
58
|
+
* await query.async();
|
|
59
|
+
* console.log(query.isLoading); // переключался в true на момент запроса
|
|
60
|
+
* console.log(query.isSuccess); // true
|
|
61
|
+
*
|
|
62
|
+
* query.invalidate();
|
|
63
|
+
* await query.async();
|
|
64
|
+
* console.log(query.isLoading); // не изменялся
|
|
65
|
+
* console.log(query.isSuccess); // остался неизменным - true
|
|
66
|
+
*
|
|
67
|
+
* console.log(query.backgroundStatus.isLoading); // переключался в true на момент обновления
|
|
68
|
+
* console.log(query.backgroundStatus.isSuccess); // true
|
|
69
|
+
*
|
|
70
|
+
* @exception isBackground:false (а так же по умолчанию) background не доступен, и равен null
|
|
71
|
+
* @exception Mutation не доступен в мутации и равен null
|
|
72
|
+
*/
|
|
73
|
+
get background() {
|
|
74
|
+
if (!this.backgroundStatusStorage) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return this.backgroundStatusStorage;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './QueryContainer';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './QueryContainer';
|
package/README.md
CHANGED
|
@@ -342,6 +342,31 @@ console.log(query.isError); // 'true'
|
|
|
342
342
|
console.log(query.error); // 'foo'
|
|
343
343
|
```
|
|
344
344
|
|
|
345
|
+
## Режим фонового обновления
|
|
346
|
+
`Query` и `InfiniteQuery` имеют режим фонового обновления. Предполагается, что будет хорошо подходить для обновления данных через websocket.
|
|
347
|
+
|
|
348
|
+
В этом режиме, основные статусные флаги `isSuccess`, `isLoading`, `isError`, `error` будут изменяться до первого успешного запроса. Последующие запросы уже будут изменять статусные флаги под полем `backgroundStatus`
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
const query = mobxQuery.createQuery(
|
|
352
|
+
['some cache key'],
|
|
353
|
+
() => Promise.resolve('foo'),
|
|
354
|
+
{ isBackground: true }
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
await query.async();
|
|
358
|
+
console.log(query.isLoading); // переключался в true на момент запроса
|
|
359
|
+
console.log(query.isSuccess); // true
|
|
360
|
+
|
|
361
|
+
query.invalidate();
|
|
362
|
+
await query.async();
|
|
363
|
+
console.log(query.isLoading); // не изменялся
|
|
364
|
+
console.log(query.isSuccess); // остался неизменным - true
|
|
365
|
+
|
|
366
|
+
console.log(query.backgroundStatus.isLoading); // переключался в true на момент обновления
|
|
367
|
+
console.log(query.backgroundStatus.isSuccess); // true
|
|
368
|
+
```
|
|
369
|
+
|
|
345
370
|
# Тестирование
|
|
346
371
|
|
|
347
372
|
## Тестирование при включенном ```enabledAutoFetch```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StorageFactory } from '../StorageFactory';
|
|
2
|
+
import { type CacheKey } from '../types';
|
|
3
|
+
export declare class StatusStorage<TError> {
|
|
4
|
+
constructor();
|
|
5
|
+
/**
|
|
6
|
+
* флаг обозначающий загрузку данных
|
|
7
|
+
*/
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* флаг обозначающий, что последний запрос был зафейлен
|
|
11
|
+
*/
|
|
12
|
+
isError: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* данные о последней ошибке
|
|
15
|
+
*/
|
|
16
|
+
error?: TError;
|
|
17
|
+
/**
|
|
18
|
+
* флаг, обозначающий успешность завершения последнего запроса
|
|
19
|
+
*/
|
|
20
|
+
isSuccess: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare class StatusStorageFactory extends StorageFactory<StatusStorage<void>> {
|
|
23
|
+
constructor();
|
|
24
|
+
getStorage: <TError>(key: CacheKey[]) => StatusStorage<TError>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { makeObservable, observable } from 'mobx';
|
|
2
|
+
import { StorageFactory } from '../StorageFactory';
|
|
3
|
+
export class StatusStorage {
|
|
4
|
+
constructor() {
|
|
5
|
+
/**
|
|
6
|
+
* флаг обозначающий загрузку данных
|
|
7
|
+
*/
|
|
8
|
+
this.isLoading = false;
|
|
9
|
+
/**
|
|
10
|
+
* флаг обозначающий, что последний запрос был зафейлен
|
|
11
|
+
*/
|
|
12
|
+
this.isError = false;
|
|
13
|
+
/**
|
|
14
|
+
* данные о последней ошибке
|
|
15
|
+
*/
|
|
16
|
+
this.error = undefined;
|
|
17
|
+
/**
|
|
18
|
+
* флаг, обозначающий успешность завершения последнего запроса
|
|
19
|
+
*/
|
|
20
|
+
this.isSuccess = false;
|
|
21
|
+
makeObservable(this, {
|
|
22
|
+
error: observable,
|
|
23
|
+
isError: observable,
|
|
24
|
+
isLoading: observable,
|
|
25
|
+
isSuccess: observable,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class StatusStorageFactory extends StorageFactory {
|
|
30
|
+
constructor() {
|
|
31
|
+
super(() => new StatusStorage());
|
|
32
|
+
this.getStorage = (key) => {
|
|
33
|
+
return this.getInternalStorage(key);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StatusStorage';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StatusStorage';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type CacheKey } from '../types';
|
|
2
|
+
export declare abstract class StorageFactory<TStorage> {
|
|
3
|
+
private readonly createStorage;
|
|
4
|
+
protected constructor(createStorage: () => TStorage);
|
|
5
|
+
/**
|
|
6
|
+
* Map хранящий инстансы хранилищ по хэшу ключа
|
|
7
|
+
*/
|
|
8
|
+
private storageMap;
|
|
9
|
+
/**
|
|
10
|
+
* фабричный метод получения/создания инстанса хранилища по ключу
|
|
11
|
+
*/
|
|
12
|
+
protected getInternalStorage: (key: CacheKey[]) => TStorage | undefined;
|
|
13
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export class StorageFactory {
|
|
2
|
+
constructor(createStorage) {
|
|
3
|
+
this.createStorage = createStorage;
|
|
4
|
+
/**
|
|
5
|
+
* Map хранящий инстансы хранилищ по хэшу ключа
|
|
6
|
+
*/
|
|
7
|
+
this.storageMap = new Map();
|
|
8
|
+
/**
|
|
9
|
+
* фабричный метод получения/создания инстанса хранилища по ключу
|
|
10
|
+
*/
|
|
11
|
+
this.getInternalStorage = (key) => {
|
|
12
|
+
const keyHash = JSON.stringify(key);
|
|
13
|
+
if (!this.storageMap.has(keyHash)) {
|
|
14
|
+
this.storageMap.set(keyHash, this.createStorage());
|
|
15
|
+
}
|
|
16
|
+
return this.storageMap.get(keyHash);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StorageFactory';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './StorageFactory';
|
package/package.json
CHANGED
package/types.d.ts
CHANGED
|
@@ -4,36 +4,36 @@ export type SyncParams<TResult, TError, TExecutorParams = void> = {
|
|
|
4
4
|
params?: TExecutorParams;
|
|
5
5
|
};
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* синхронный метод получения данных
|
|
8
8
|
*/
|
|
9
9
|
export type Sync<TResult, TError, TExecutorParams = void> = (params?: SyncParams<TResult, TError, TExecutorParams>) => void;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* асинхронный метод получения данных
|
|
12
12
|
*/
|
|
13
13
|
export type Async<TResult, TExecutorParams = void> = (params: TExecutorParams) => Promise<TResult>;
|
|
14
14
|
export type QueryBaseActions<TResult, TError, TExecutorParams = void> = {
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* синхронный метод получения данных
|
|
17
17
|
*/
|
|
18
18
|
sync: Sync<TResult, TError, TExecutorParams>;
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* асинхронный метод получения данных
|
|
21
21
|
*/
|
|
22
22
|
async: Async<TResult, TExecutorParams>;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
24
|
+
* флаг обозначающий загрузку данных
|
|
25
25
|
*/
|
|
26
26
|
isLoading: boolean;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* флаг обозначающий, что последний запрос был зафейлен
|
|
29
29
|
*/
|
|
30
30
|
isError: boolean;
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* данные о последней ошибке
|
|
33
33
|
*/
|
|
34
34
|
error?: TError;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* флаг обзначающий, что последний запрос был успешно выполнен
|
|
37
37
|
*/
|
|
38
38
|
isSuccess: boolean;
|
|
39
39
|
/**
|
|
@@ -42,13 +42,13 @@ export type QueryBaseActions<TResult, TError, TExecutorParams = void> = {
|
|
|
42
42
|
isIdle: boolean;
|
|
43
43
|
};
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
46
|
-
* @
|
|
47
|
-
* @
|
|
45
|
+
* политика получения данных.
|
|
46
|
+
* @enum cache-first - данные сначала берутся из кеша, если их нет, тогда идет обращение к сети, ответ записывается в кэш
|
|
47
|
+
* @enum network-only - данные всегда беруться из сети, при этом ответ записывается в кэш
|
|
48
48
|
*/
|
|
49
49
|
export type FetchPolicy = 'network-only' | 'cache-first';
|
|
50
50
|
/**
|
|
51
|
-
*
|
|
51
|
+
* ключ для кешированя
|
|
52
52
|
*/
|
|
53
53
|
export type CacheKey = string | number | boolean | null | undefined | CacheKey[] | {
|
|
54
54
|
[key: string]: CacheKey;
|