@astral/mobx-query 0.1.4 → 1.0.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/README.md +323 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1 +1,324 @@
|
|
|
1
|
+
# @astral/mobx-query
|
|
1
2
|
|
|
3
|
+
Библиотека для кеширования запросов.
|
|
4
|
+
|
|
5
|
+
Особенности:
|
|
6
|
+
- Ориентирована на специфику frontend приложений
|
|
7
|
+
- Для обеспечения реактивности используется [mobx](https://mobx.js.org/)
|
|
8
|
+
- По идеологии использования схожа с [@tanstack/react-query](https://www.npmjs.com/package/@tanstack/react-query/)
|
|
9
|
+
- TS only, totally no `any`
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Table of contents
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [Basic meaning](#basic-meaning)
|
|
16
|
+
- [Варианты использования query](#варианты-использования-query)
|
|
17
|
+
- [Ручной синхронный](#1-ручной-синхронный)
|
|
18
|
+
- [Ручной асинхронный](#2-ручной-асинхронный)
|
|
19
|
+
- [Автоматический](#3-автоматический)
|
|
20
|
+
- [Инвалидация данных](#инвалидация-данных)
|
|
21
|
+
- [Особенности инвалидации](#особенности-инвалидации)
|
|
22
|
+
- [InfiniteQuery](#infinitequery)
|
|
23
|
+
- [isEndReached](#isendreached)
|
|
24
|
+
- [Mutation](#mutation)
|
|
25
|
+
- [Sync вариация](#sync-вариация)
|
|
26
|
+
- [Async вариация](#async-вариация)
|
|
27
|
+
- [Fetch policy](#fetchpolicy)
|
|
28
|
+
- [Вспомогательные флаги и поля](#вспомогательные-флаги-и-поля)
|
|
29
|
+
- [isLoading](#isloading)
|
|
30
|
+
- [isSuccess](#issuccess)
|
|
31
|
+
- [isError](#iserror)
|
|
32
|
+
- [error](#error)
|
|
33
|
+
|
|
34
|
+
# Installation
|
|
35
|
+
|
|
36
|
+
```shell
|
|
37
|
+
npm i --save @astral/mobx-query
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```shell
|
|
41
|
+
yarn add @astral/mobx-query
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# Basic meaning
|
|
47
|
+
- executor - исполнитель запроса, который будет совершать запрос. Второй аргумент при создании query
|
|
48
|
+
- enabledAutoFetch - включает автоматический запрос данных при обращении к полю `data`.
|
|
49
|
+
- fetchPolicy - политика, говорящая о том, как следует работать с новыми запросами
|
|
50
|
+
- 'cache-first' - политика применяемая по умолчанию, при отсутствии данных в памяти, будет исполнен executor, его ответ запишется в кеш, и при последующих обращениях данные будут взяты из кеша
|
|
51
|
+
- 'network-only' - каждый запрос будет приводить к вызову executor, его ответ будет записан в кеш(для использования в cache-first)
|
|
52
|
+
|
|
53
|
+
# Basic usage
|
|
54
|
+
|
|
55
|
+
Для начала вам потребуется создать инстанс кеш сервиса
|
|
56
|
+
```ts
|
|
57
|
+
import { MobxQuery } from '@astral/mobx-query';
|
|
58
|
+
|
|
59
|
+
const mobxQuery = new MobxQuery({
|
|
60
|
+
onError: (error) => {
|
|
61
|
+
console.log(error); // место для вашей обработки ошибок по умолчанию, опционально
|
|
62
|
+
},
|
|
63
|
+
fetchPolicy: 'cache-first', // 'cache-first' по умолчанию, опционально
|
|
64
|
+
enabledAutoFetch: false, // false по умолчанию, опционально
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
# Варианты использования query
|
|
69
|
+
## 1. Ручной синхронный.
|
|
70
|
+
|
|
71
|
+
Можно вызывать встроенный метод `sync`, передавая в него колбэк опциональные параметры `onSucess` и `onError`. В onSuccess будут переданы полученные данные от успешного запроса, а в `onError`, соответственно, будет переданы данные ошибки в случае провального запроса.
|
|
72
|
+
Если при вызове обработчик `onError` не был передан, вызовется стандартный, переданный при создании MobxQuery инстанса.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
const query = mobxQuery.createQuery(
|
|
76
|
+
['some cache key'],
|
|
77
|
+
() => Promise.resolve('foo'),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
query.sync({
|
|
81
|
+
onSuccess: (data) => {
|
|
82
|
+
console.log(data); // место для реагирования на ответ
|
|
83
|
+
},
|
|
84
|
+
onError: (error) => {
|
|
85
|
+
console.log(error); // место для вашей ошибки
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**[Пример в sandbox](https://codesandbox.io/s/adoring-wing-z8s9ng)**
|
|
91
|
+
|
|
92
|
+
## 2. Ручной асинхронный.
|
|
93
|
+
|
|
94
|
+
Можно вызвать встроенный метод async. Возвращает промис, соответственно в then попадут данные успешного запроса.
|
|
95
|
+
`Будьте внимательны, используя метод "async", позаботьтесь о добавлении ".catch", иначе ошибка запроса попадет в глобальный exception`.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
const query = mobxQuery.createQuery(
|
|
99
|
+
['some cache key'],
|
|
100
|
+
() => Promise.resolve('foo'),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
query
|
|
104
|
+
.async()
|
|
105
|
+
.then((data) => {
|
|
106
|
+
console.log(data); // место для реагирования на ответ
|
|
107
|
+
})
|
|
108
|
+
.catch((e) => {
|
|
109
|
+
console.log(e); // место для вашей ошибки
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
**[Пример в sandbox](https://codesandbox.io/s/mobx-query-simple-async-k75tfg)**
|
|
113
|
+
|
|
114
|
+
## 3. Автоматический.
|
|
115
|
+
|
|
116
|
+
При создании query, предусмотрен вариант автоматического запроса при обращении к полю `data` из `query`. Требуется активация флага `enabledAutoFetch` при создании query, либо установка стандартного значения, при создании MobxQuery инстанса.
|
|
117
|
+
Т.е. благодаря реактивности предоставляемой `mobx`, пока не произойдет считывания поля `data` или же не будут вызваны `sync/async` методы, запрос данных так же не произойдет.
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
import { observer } from 'mobx-react-lite';
|
|
121
|
+
|
|
122
|
+
const query = mobxQuery.createQuery(
|
|
123
|
+
['some cache key'],
|
|
124
|
+
() => Promise.resolve('foo'),
|
|
125
|
+
{ enabledAutoFetch: true }
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const MyComponent = observer(() => <div>{query.data}</div>) // <div>foo</div>
|
|
129
|
+
```
|
|
130
|
+
**[Пример в sandbox](https://codesandbox.io/s/happy-cherry-ytqgry)**
|
|
131
|
+
|
|
132
|
+
# Инвалидация данных
|
|
133
|
+
Существует необходимость инвалидировать данные, типичным примером являются [CRUD операции](https://ru.wikipedia.org/wiki/CRUD).
|
|
134
|
+
В контексте нашей библиотеки, инвалидация подразумевает под собой отметку для query, означающую, что данные устарели, и их необходимо обновить.
|
|
135
|
+
Для корректной работы инвалидации, при создании query требуется использование `ключа`.
|
|
136
|
+
Ключ для создания может быть как примитивом, так и объектом. Главное, чтобы они были подходящими для JSON сериализации.
|
|
137
|
+
|
|
138
|
+
Инстанс MobxQuery содержит специальный метод `invalidate`, принимающий в качестве аргумента `массив ключей`.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const query = mobxQuery.createQuery(
|
|
142
|
+
['some cache key'],
|
|
143
|
+
() => Promise.resolve('foo'),
|
|
144
|
+
{ enabledAutoFetch: true }
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
mobxQuery.invalidate(['some cache key'])
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**[Пример в sandbox](https://codesandbox.io/s/mobx-query-invalidate-query-hhcngr)**
|
|
151
|
+
|
|
152
|
+
## Особенности инвалидации
|
|
153
|
+
- Как при создании query, так и при инвалидации, нужно использовать массив ключей. Предполагается, что query может быть инвалидирован по нескольким ключам
|
|
154
|
+
```ts
|
|
155
|
+
const query = mobxQuery.createQuery(
|
|
156
|
+
['key one', 'key two'], // ключ - массив строк
|
|
157
|
+
() => Promise.resolve('foo'),
|
|
158
|
+
{ enabledAutoFetch: true }
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
mobxQuery.invalidate(['key two']); // query будет инвалидирован
|
|
162
|
+
mobxQuery.invalidate(['key one']); // query будет инвалидирован
|
|
163
|
+
```
|
|
164
|
+
Но, стоит учитывать, что ключом является цельный элемент массива, а не составляющие элемента
|
|
165
|
+
```ts
|
|
166
|
+
const query = mobxQuery.createQuery(
|
|
167
|
+
[['key one', 'key two']], // ключ - двумерный массив строк
|
|
168
|
+
() => Promise.resolve('foo'),
|
|
169
|
+
{ enabledAutoFetch: true }
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
mobxQuery.invalidate(['key one']); // ключ не совпадает, query НЕ будет инвалидирован
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
- Инвалидация будет происходить только для query, поле `data` которых считывается в данный момент. Для query, `data` которых будут отрендерены позже, запрос произойдет только в момент использования. Для превентивного обновления данных потребуется последовательное использование `sync/async` методов сразу после `invalidate`.
|
|
176
|
+
|
|
177
|
+
# InfiniteQuery
|
|
178
|
+
Существует необходимость постепенного запроса массивов данных, в постраничном режиме. Типичный пример, инфинити скролл, когда новая пачка данных запрашивается, в момент когда пользователь докрутил список до конца.
|
|
179
|
+
Для удобства, мы создали специальный `query`, который содержит дополнительный метод `fetchMore` и при вызове оного, происходит запрос с увеличенными счетчиками. Данные ответа на этот запрос, будут сконкатенированы с уже имеющимся. В случае, если количество данных меньше, чем длина страницы, будет считаться что мы дошли до конца списка.
|
|
180
|
+
В `executor` будет передан объект с `offset` - количество элементов отступа от начала списка, и `count` - количество элементов на одну страницу.
|
|
181
|
+
|
|
182
|
+
Значение `count` и увеличение `offset` регулируется опциональным параметром `incrementCount` при создании `query`. По умолчанию равен `30`.
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { when } from 'mobx';
|
|
186
|
+
|
|
187
|
+
const query = mobxQuery.createInfiniteQuery(
|
|
188
|
+
['some cache key'],
|
|
189
|
+
({ offset, count }) => {
|
|
190
|
+
// можно использовать "offset/count" для необходимых преобразований и последующего запроса к api
|
|
191
|
+
return Promise.resolve(['foo'])
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
incrementCount: 30, // опционально, по умолчанию 30
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
await query.async();
|
|
199
|
+
|
|
200
|
+
console.log(query.data); // ['foo']
|
|
201
|
+
|
|
202
|
+
query.fetchMore();
|
|
203
|
+
await when(() => !query.isLoading); // ждем фоновой загрузки
|
|
204
|
+
|
|
205
|
+
console.log(query.data); // ['foo', 'foo']
|
|
206
|
+
```
|
|
207
|
+
## isEndReached
|
|
208
|
+
Для определения того, что мы все таки достигли конца списка, присутствует флаг `isEndReached`.
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const query = mobxQuery.createInfiniteQuery(
|
|
212
|
+
['some cache key'],
|
|
213
|
+
() => Promise.resolve([]),
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
await query.async();
|
|
217
|
+
|
|
218
|
+
console.log(query.isEndReached); // true
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**[Пример в sandbox](https://codesandbox.io/s/mobx-query-infinityquery-sdcc6g)**
|
|
222
|
+
|
|
223
|
+
# Mutation
|
|
224
|
+
Для изменения данных необходимо использовать mutation. Ответы Mutation не кэшируются.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
const mutation = mobxQuery.createMutation(
|
|
228
|
+
(params) => {
|
|
229
|
+
console.log(params); // при необходмости, можем использовать опциональные параметры
|
|
230
|
+
return Promise.resolve('foo');
|
|
231
|
+
},
|
|
232
|
+
);
|
|
233
|
+
```
|
|
234
|
+
**[Пример в sandbox](https://codesandbox.io/s/mobx-query-mutation-p2pnct)**
|
|
235
|
+
|
|
236
|
+
## async вариация
|
|
237
|
+
```ts
|
|
238
|
+
mutation
|
|
239
|
+
.async('bar') // тут, по нашему примеру, увидим консоль 'bar'
|
|
240
|
+
.then((data) => {
|
|
241
|
+
console.log(data) // а тут уже 'foo'
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## sync вариация
|
|
246
|
+
```ts
|
|
247
|
+
mutation.sync({
|
|
248
|
+
params: 'bar',
|
|
249
|
+
onSuccess: (data) => {
|
|
250
|
+
console.log(data) // а тут уже 'foo'
|
|
251
|
+
}
|
|
252
|
+
}); // тут, по нашему примеру, увидим консоль 'bar'
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
# fetchPolicy
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
const cacheFirstQuery = mobxQuery.createQuery(
|
|
259
|
+
['cache-first key'],
|
|
260
|
+
() => {
|
|
261
|
+
console.log('cache-first request');
|
|
262
|
+
return Promise.resolve('foo');
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
fetchPolicy: 'cache-first',
|
|
266
|
+
}
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
const networkOnlyQuery = mobxQuery.createQuery(
|
|
270
|
+
['network-only key'],
|
|
271
|
+
() => {
|
|
272
|
+
console.log('network-only request');
|
|
273
|
+
return Promise.resolve('bar');
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
fetchPolicy: 'network-only',
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
await cacheFirstQuery.async(); // увидим консоль 'cache-first request'
|
|
281
|
+
await networkOnlyQuery.async(); // увидим консоль 'network-only request'
|
|
282
|
+
|
|
283
|
+
await cacheFirstQuery.async(); // вызова executor не произойдет, и консоль не выведется
|
|
284
|
+
await networkOnlyQuery.async(); // вновь увидим консоль 'network-only request'
|
|
285
|
+
|
|
286
|
+
const duplicateCacheFirstQuery = mobxQuery.createQuery(
|
|
287
|
+
['cache-first key'], // использован тот же самый ключ, что и для cacheFirstQuery
|
|
288
|
+
() => {
|
|
289
|
+
console.log('duplicate cache-first request');
|
|
290
|
+
return Promise.resolve('foo');
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
await duplicateCacheFirstQuery.async(); // вызова executor не произойдет, и консоль не выведется
|
|
295
|
+
```
|
|
296
|
+
**[Пример в sandbox](https://codesandbox.io/s/mobx-query-fetchpolicy-wvh8jl)**
|
|
297
|
+
|
|
298
|
+
# Вспомогательные флаги и поля
|
|
299
|
+
`Query`, `InfiniteQuery` и `Mutation` имеют одинаковый набор вспомогательных флагов и полей, работающих по единому принципу.
|
|
300
|
+
|
|
301
|
+
## isLoading
|
|
302
|
+
Boolean флаг, указывающий на процесс выполнения запроса
|
|
303
|
+
## isSuccess
|
|
304
|
+
Boolean флаг, указывающий на успешное выполнение запроса
|
|
305
|
+
## isError
|
|
306
|
+
Boolean флаг, указывающий на провалившийся запрос
|
|
307
|
+
## error
|
|
308
|
+
Поле, содержащее информацию о последней ошибке
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
const query = mobxQuery.createQuery(
|
|
312
|
+
['some cache key'],
|
|
313
|
+
() => Promise.reject('foo'),
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
await query
|
|
317
|
+
.async()
|
|
318
|
+
.catch((e) => {
|
|
319
|
+
console.log(e); // 'foo'
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
console.log(query.isError); // 'true'
|
|
323
|
+
console.log(query.error); // 'foo'
|
|
324
|
+
```
|