@spoosh/react 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +222 -0
- package/dist/index.d.mts +411 -0
- package/dist/index.d.ts +411 -0
- package/dist/index.js +549 -0
- package/dist/index.mjs +555 -0
- package/package.json +49 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/index.ts
|
|
23
|
+
var src_exports = {};
|
|
24
|
+
__export(src_exports, {
|
|
25
|
+
createReactSpoosh: () => createReactSpoosh,
|
|
26
|
+
createUseInfiniteRead: () => createUseInfiniteRead,
|
|
27
|
+
createUseRead: () => createUseRead,
|
|
28
|
+
createUseWrite: () => createUseWrite
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(src_exports);
|
|
31
|
+
|
|
32
|
+
// src/useRead/index.ts
|
|
33
|
+
var import_react = require("react");
|
|
34
|
+
var import_core = require("@spoosh/core");
|
|
35
|
+
function createUseRead(options) {
|
|
36
|
+
const { api, stateManager, eventEmitter, pluginExecutor } = options;
|
|
37
|
+
return function useRead(readFn, readOptions) {
|
|
38
|
+
const {
|
|
39
|
+
enabled = true,
|
|
40
|
+
tags,
|
|
41
|
+
additionalTags,
|
|
42
|
+
...pluginOpts
|
|
43
|
+
} = readOptions ?? {};
|
|
44
|
+
const hookId = (0, import_react.useId)();
|
|
45
|
+
const selectorResultRef = (0, import_react.useRef)({
|
|
46
|
+
call: null,
|
|
47
|
+
selector: null
|
|
48
|
+
});
|
|
49
|
+
const selectorProxy = (0, import_core.createSelectorProxy)((result2) => {
|
|
50
|
+
selectorResultRef.current = result2;
|
|
51
|
+
});
|
|
52
|
+
readFn(selectorProxy);
|
|
53
|
+
const capturedCall = selectorResultRef.current.call;
|
|
54
|
+
if (!capturedCall) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
"useRead requires calling an HTTP method ($get). Example: useRead((api) => api.posts.$get())"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
const requestOptions = capturedCall.options;
|
|
60
|
+
const resolvedPath = (0, import_core.resolvePath)(capturedCall.path, requestOptions?.params);
|
|
61
|
+
const resolvedTags = (0, import_core.resolveTags)({ tags, additionalTags }, resolvedPath);
|
|
62
|
+
const queryKey = stateManager.createQueryKey({
|
|
63
|
+
path: capturedCall.path,
|
|
64
|
+
method: capturedCall.method,
|
|
65
|
+
options: capturedCall.options
|
|
66
|
+
});
|
|
67
|
+
const controllerRef = (0, import_react.useRef)(null);
|
|
68
|
+
const lifecycleRef = (0, import_react.useRef)({
|
|
69
|
+
initialized: false,
|
|
70
|
+
prevContext: null
|
|
71
|
+
});
|
|
72
|
+
if (controllerRef.current && controllerRef.current.queryKey !== queryKey) {
|
|
73
|
+
lifecycleRef.current.prevContext = controllerRef.current.controller.getContext();
|
|
74
|
+
}
|
|
75
|
+
if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
|
|
76
|
+
const controller2 = (0, import_core.createOperationController)({
|
|
77
|
+
operationType: "read",
|
|
78
|
+
path: capturedCall.path,
|
|
79
|
+
method: capturedCall.method,
|
|
80
|
+
tags: resolvedTags,
|
|
81
|
+
requestOptions: capturedCall.options,
|
|
82
|
+
stateManager,
|
|
83
|
+
eventEmitter,
|
|
84
|
+
pluginExecutor,
|
|
85
|
+
hookId,
|
|
86
|
+
fetchFn: async (fetchOpts) => {
|
|
87
|
+
let current = api;
|
|
88
|
+
for (const segment of resolvedPath) {
|
|
89
|
+
current = current[segment];
|
|
90
|
+
}
|
|
91
|
+
const method = current[capturedCall.method];
|
|
92
|
+
return method(fetchOpts);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
controllerRef.current = { controller: controller2, queryKey };
|
|
96
|
+
}
|
|
97
|
+
const controller = controllerRef.current.controller;
|
|
98
|
+
controller.setPluginOptions(pluginOpts);
|
|
99
|
+
const state = (0, import_react.useSyncExternalStore)(
|
|
100
|
+
controller.subscribe,
|
|
101
|
+
controller.getState,
|
|
102
|
+
controller.getState
|
|
103
|
+
);
|
|
104
|
+
const [requestState, setRequestState] = (0, import_react.useState)({ isPending: false, error: void 0 });
|
|
105
|
+
const abortRef = (0, import_react.useRef)(controller.abort);
|
|
106
|
+
abortRef.current = controller.abort;
|
|
107
|
+
const pluginOptsKey = JSON.stringify(pluginOpts);
|
|
108
|
+
const executeWithTracking = (0, import_react.useCallback)(
|
|
109
|
+
async (force = false) => {
|
|
110
|
+
setRequestState((prev) => ({ ...prev, isPending: true }));
|
|
111
|
+
try {
|
|
112
|
+
const response = await controller.execute(void 0, { force });
|
|
113
|
+
if (response.error) {
|
|
114
|
+
setRequestState({ isPending: false, error: response.error });
|
|
115
|
+
} else {
|
|
116
|
+
setRequestState({ isPending: false, error: void 0 });
|
|
117
|
+
}
|
|
118
|
+
return response;
|
|
119
|
+
} catch (err) {
|
|
120
|
+
setRequestState({ isPending: false, error: err });
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
[controller]
|
|
125
|
+
);
|
|
126
|
+
(0, import_react.useEffect)(() => {
|
|
127
|
+
return () => {
|
|
128
|
+
controllerRef.current?.controller.unmount();
|
|
129
|
+
lifecycleRef.current.initialized = false;
|
|
130
|
+
};
|
|
131
|
+
}, []);
|
|
132
|
+
(0, import_react.useEffect)(() => {
|
|
133
|
+
if (!enabled) return;
|
|
134
|
+
const { initialized, prevContext } = lifecycleRef.current;
|
|
135
|
+
if (!initialized) {
|
|
136
|
+
controller.mount();
|
|
137
|
+
lifecycleRef.current.initialized = true;
|
|
138
|
+
} else if (prevContext) {
|
|
139
|
+
controller.update(prevContext);
|
|
140
|
+
lifecycleRef.current.prevContext = null;
|
|
141
|
+
}
|
|
142
|
+
executeWithTracking(false);
|
|
143
|
+
const unsubRefetch = eventEmitter.on("refetch", (event) => {
|
|
144
|
+
if (event.queryKey === queryKey) {
|
|
145
|
+
executeWithTracking(true);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
const unsubInvalidate = eventEmitter.on(
|
|
149
|
+
"invalidate",
|
|
150
|
+
(invalidatedTags) => {
|
|
151
|
+
const hasMatch = invalidatedTags.some(
|
|
152
|
+
(tag) => resolvedTags.includes(tag)
|
|
153
|
+
);
|
|
154
|
+
if (hasMatch) {
|
|
155
|
+
executeWithTracking(true);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
);
|
|
159
|
+
return () => {
|
|
160
|
+
unsubRefetch();
|
|
161
|
+
unsubInvalidate();
|
|
162
|
+
};
|
|
163
|
+
}, [queryKey, enabled]);
|
|
164
|
+
(0, import_react.useEffect)(() => {
|
|
165
|
+
if (!enabled || !lifecycleRef.current.initialized) return;
|
|
166
|
+
const prevContext = controller.getContext();
|
|
167
|
+
controller.update(prevContext);
|
|
168
|
+
}, [pluginOptsKey]);
|
|
169
|
+
const abort = (0, import_react.useCallback)(() => {
|
|
170
|
+
abortRef.current();
|
|
171
|
+
}, []);
|
|
172
|
+
const refetch = (0, import_react.useCallback)(() => {
|
|
173
|
+
return executeWithTracking(true);
|
|
174
|
+
}, [executeWithTracking]);
|
|
175
|
+
const entry = stateManager.getCache(queryKey);
|
|
176
|
+
const pluginResultData = entry?.pluginResult ? Object.fromEntries(entry.pluginResult) : {};
|
|
177
|
+
const opts = capturedCall.options;
|
|
178
|
+
const inputInner = {};
|
|
179
|
+
if (opts?.query !== void 0) {
|
|
180
|
+
inputInner.query = opts.query;
|
|
181
|
+
}
|
|
182
|
+
if (opts?.body !== void 0) {
|
|
183
|
+
inputInner.body = opts.body;
|
|
184
|
+
}
|
|
185
|
+
if (opts?.formData !== void 0) {
|
|
186
|
+
inputInner.formData = opts.formData;
|
|
187
|
+
}
|
|
188
|
+
if (opts?.params !== void 0) {
|
|
189
|
+
inputInner.params = opts.params;
|
|
190
|
+
}
|
|
191
|
+
const inputField = Object.keys(inputInner).length > 0 ? { input: inputInner } : {};
|
|
192
|
+
const hasData = state.data !== void 0;
|
|
193
|
+
const loading = requestState.isPending && !hasData;
|
|
194
|
+
const fetching = requestState.isPending;
|
|
195
|
+
const result = {
|
|
196
|
+
...pluginResultData,
|
|
197
|
+
...inputField,
|
|
198
|
+
data: state.data,
|
|
199
|
+
error: requestState.error ?? state.error,
|
|
200
|
+
loading,
|
|
201
|
+
fetching,
|
|
202
|
+
abort,
|
|
203
|
+
refetch
|
|
204
|
+
};
|
|
205
|
+
return result;
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/useWrite/index.ts
|
|
210
|
+
var import_react2 = require("react");
|
|
211
|
+
var import_core2 = require("@spoosh/core");
|
|
212
|
+
function createUseWrite(options) {
|
|
213
|
+
const { api, stateManager, pluginExecutor, eventEmitter } = options;
|
|
214
|
+
return function useWrite(writeFn) {
|
|
215
|
+
const hookId = (0, import_react2.useId)();
|
|
216
|
+
const selectorResultRef = (0, import_react2.useRef)({
|
|
217
|
+
call: null,
|
|
218
|
+
selector: null
|
|
219
|
+
});
|
|
220
|
+
const selectorProxy = (0, import_core2.createSelectorProxy)((result2) => {
|
|
221
|
+
selectorResultRef.current = result2;
|
|
222
|
+
});
|
|
223
|
+
writeFn(selectorProxy);
|
|
224
|
+
const selectedEndpoint = selectorResultRef.current.selector;
|
|
225
|
+
if (!selectedEndpoint) {
|
|
226
|
+
throw new Error(
|
|
227
|
+
"useWrite requires selecting an HTTP method ($post, $put, $patch, $delete). Example: useWrite((api) => api.posts.$post)"
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
const queryKey = stateManager.createQueryKey({
|
|
231
|
+
path: selectedEndpoint.path,
|
|
232
|
+
method: selectedEndpoint.method,
|
|
233
|
+
options: void 0
|
|
234
|
+
});
|
|
235
|
+
const controllerRef = (0, import_react2.useRef)(null);
|
|
236
|
+
if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
|
|
237
|
+
controllerRef.current = {
|
|
238
|
+
controller: (0, import_core2.createOperationController)({
|
|
239
|
+
operationType: "write",
|
|
240
|
+
path: selectedEndpoint.path,
|
|
241
|
+
method: selectedEndpoint.method,
|
|
242
|
+
tags: [],
|
|
243
|
+
stateManager,
|
|
244
|
+
eventEmitter,
|
|
245
|
+
pluginExecutor,
|
|
246
|
+
hookId,
|
|
247
|
+
fetchFn: async (fetchOpts) => {
|
|
248
|
+
const params = fetchOpts?.params;
|
|
249
|
+
const resolvedPath = (0, import_core2.resolvePath)(selectedEndpoint.path, params);
|
|
250
|
+
let current = api;
|
|
251
|
+
for (const segment of resolvedPath) {
|
|
252
|
+
current = current[segment];
|
|
253
|
+
}
|
|
254
|
+
const method = current[selectedEndpoint.method];
|
|
255
|
+
return method(fetchOpts);
|
|
256
|
+
}
|
|
257
|
+
}),
|
|
258
|
+
queryKey
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const controller = controllerRef.current.controller;
|
|
262
|
+
const state = (0, import_react2.useSyncExternalStore)(
|
|
263
|
+
controller.subscribe,
|
|
264
|
+
controller.getState,
|
|
265
|
+
controller.getState
|
|
266
|
+
);
|
|
267
|
+
const [lastTriggerOptions, setLastTriggerOptions] = (0, import_react2.useState)(void 0);
|
|
268
|
+
const [requestState, setRequestState] = (0, import_react2.useState)({ isPending: false, error: void 0 });
|
|
269
|
+
const reset = (0, import_react2.useCallback)(() => {
|
|
270
|
+
stateManager.deleteCache(queryKey);
|
|
271
|
+
setRequestState({ isPending: false, error: void 0 });
|
|
272
|
+
}, [queryKey]);
|
|
273
|
+
const abort = (0, import_react2.useCallback)(() => {
|
|
274
|
+
controller.abort();
|
|
275
|
+
}, []);
|
|
276
|
+
const trigger = (0, import_react2.useCallback)(
|
|
277
|
+
async (triggerOptions) => {
|
|
278
|
+
setLastTriggerOptions(triggerOptions);
|
|
279
|
+
setRequestState((prev) => ({ ...prev, isPending: true }));
|
|
280
|
+
const params = triggerOptions?.params;
|
|
281
|
+
const resolvedPath = (0, import_core2.resolvePath)(selectedEndpoint.path, params);
|
|
282
|
+
const tags = (0, import_core2.resolveTags)(triggerOptions, resolvedPath);
|
|
283
|
+
controller.setPluginOptions({ ...triggerOptions, tags });
|
|
284
|
+
try {
|
|
285
|
+
const response = await controller.execute(triggerOptions, {
|
|
286
|
+
force: true
|
|
287
|
+
});
|
|
288
|
+
if (response.error) {
|
|
289
|
+
setRequestState({ isPending: false, error: response.error });
|
|
290
|
+
} else {
|
|
291
|
+
setRequestState({ isPending: false, error: void 0 });
|
|
292
|
+
}
|
|
293
|
+
return response;
|
|
294
|
+
} catch (err) {
|
|
295
|
+
setRequestState({ isPending: false, error: err });
|
|
296
|
+
throw err;
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
[selectedEndpoint.path]
|
|
300
|
+
);
|
|
301
|
+
const entry = stateManager.getCache(queryKey);
|
|
302
|
+
const pluginResultData = entry?.pluginResult ? Object.fromEntries(entry.pluginResult) : {};
|
|
303
|
+
const opts = lastTriggerOptions;
|
|
304
|
+
const inputInner = {};
|
|
305
|
+
if (opts?.query !== void 0) {
|
|
306
|
+
inputInner.query = opts.query;
|
|
307
|
+
}
|
|
308
|
+
if (opts?.body !== void 0) {
|
|
309
|
+
inputInner.body = opts.body;
|
|
310
|
+
}
|
|
311
|
+
if (opts?.formData !== void 0) {
|
|
312
|
+
inputInner.formData = opts.formData;
|
|
313
|
+
}
|
|
314
|
+
if (opts?.params !== void 0) {
|
|
315
|
+
inputInner.params = opts.params;
|
|
316
|
+
}
|
|
317
|
+
const inputField = Object.keys(inputInner).length > 0 ? { input: inputInner } : {};
|
|
318
|
+
const loading = requestState.isPending;
|
|
319
|
+
const result = {
|
|
320
|
+
trigger,
|
|
321
|
+
...pluginResultData,
|
|
322
|
+
...inputField,
|
|
323
|
+
data: state.data,
|
|
324
|
+
error: requestState.error ?? state.error,
|
|
325
|
+
loading,
|
|
326
|
+
reset,
|
|
327
|
+
abort
|
|
328
|
+
};
|
|
329
|
+
return result;
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/useInfiniteRead/index.ts
|
|
334
|
+
var import_react3 = require("react");
|
|
335
|
+
var import_core3 = require("@spoosh/core");
|
|
336
|
+
function createUseInfiniteRead(options) {
|
|
337
|
+
const { api, stateManager, eventEmitter, pluginExecutor } = options;
|
|
338
|
+
return function useInfiniteRead(readFn, readOptions) {
|
|
339
|
+
const {
|
|
340
|
+
enabled = true,
|
|
341
|
+
tags,
|
|
342
|
+
additionalTags,
|
|
343
|
+
canFetchNext,
|
|
344
|
+
nextPageRequest,
|
|
345
|
+
merger,
|
|
346
|
+
canFetchPrev,
|
|
347
|
+
prevPageRequest,
|
|
348
|
+
...pluginOpts
|
|
349
|
+
} = readOptions;
|
|
350
|
+
const hookId = (0, import_react3.useId)();
|
|
351
|
+
const selectorResultRef = (0, import_react3.useRef)({
|
|
352
|
+
call: null,
|
|
353
|
+
selector: null
|
|
354
|
+
});
|
|
355
|
+
const selectorProxy = (0, import_core3.createSelectorProxy)((result2) => {
|
|
356
|
+
selectorResultRef.current = result2;
|
|
357
|
+
});
|
|
358
|
+
readFn(selectorProxy);
|
|
359
|
+
const capturedCall = selectorResultRef.current.call;
|
|
360
|
+
if (!capturedCall) {
|
|
361
|
+
throw new Error(
|
|
362
|
+
"useInfiniteRead requires calling an HTTP method ($get). Example: useInfiniteRead((api) => api.posts.$get())"
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
const requestOptions = capturedCall.options;
|
|
366
|
+
const initialRequest = {
|
|
367
|
+
query: requestOptions?.query,
|
|
368
|
+
params: requestOptions?.params,
|
|
369
|
+
body: requestOptions?.body
|
|
370
|
+
};
|
|
371
|
+
const baseOptionsForKey = {
|
|
372
|
+
...capturedCall.options,
|
|
373
|
+
query: void 0,
|
|
374
|
+
params: void 0,
|
|
375
|
+
body: void 0
|
|
376
|
+
};
|
|
377
|
+
const resolvedPath = (0, import_core3.resolvePath)(capturedCall.path, requestOptions?.params);
|
|
378
|
+
const resolvedTags = (0, import_core3.resolveTags)({ tags, additionalTags }, resolvedPath);
|
|
379
|
+
const canFetchNextRef = (0, import_react3.useRef)(canFetchNext);
|
|
380
|
+
const canFetchPrevRef = (0, import_react3.useRef)(canFetchPrev);
|
|
381
|
+
const nextPageRequestRef = (0, import_react3.useRef)(nextPageRequest);
|
|
382
|
+
const prevPageRequestRef = (0, import_react3.useRef)(prevPageRequest);
|
|
383
|
+
const mergerRef = (0, import_react3.useRef)(merger);
|
|
384
|
+
canFetchNextRef.current = canFetchNext;
|
|
385
|
+
canFetchPrevRef.current = canFetchPrev;
|
|
386
|
+
nextPageRequestRef.current = nextPageRequest;
|
|
387
|
+
prevPageRequestRef.current = prevPageRequest;
|
|
388
|
+
mergerRef.current = merger;
|
|
389
|
+
const queryKey = stateManager.createQueryKey({
|
|
390
|
+
path: capturedCall.path,
|
|
391
|
+
method: capturedCall.method,
|
|
392
|
+
options: baseOptionsForKey
|
|
393
|
+
});
|
|
394
|
+
const controllerRef = (0, import_react3.useRef)(null);
|
|
395
|
+
if (!controllerRef.current || controllerRef.current.queryKey !== queryKey) {
|
|
396
|
+
controllerRef.current = {
|
|
397
|
+
controller: (0, import_core3.createInfiniteReadController)({
|
|
398
|
+
path: capturedCall.path,
|
|
399
|
+
method: capturedCall.method,
|
|
400
|
+
tags: resolvedTags,
|
|
401
|
+
initialRequest,
|
|
402
|
+
baseOptionsForKey,
|
|
403
|
+
canFetchNext: (ctx) => canFetchNextRef.current(ctx),
|
|
404
|
+
canFetchPrev: canFetchPrev ? (ctx) => canFetchPrevRef.current?.(ctx) ?? false : void 0,
|
|
405
|
+
nextPageRequest: (ctx) => nextPageRequestRef.current(ctx),
|
|
406
|
+
prevPageRequest: prevPageRequest ? (ctx) => prevPageRequestRef.current?.(ctx) ?? {} : void 0,
|
|
407
|
+
merger: (responses) => mergerRef.current(responses),
|
|
408
|
+
stateManager,
|
|
409
|
+
eventEmitter,
|
|
410
|
+
pluginExecutor,
|
|
411
|
+
hookId,
|
|
412
|
+
fetchFn: async (opts, signal) => {
|
|
413
|
+
const fetchPath = (0, import_core3.resolvePath)(capturedCall.path, opts.params);
|
|
414
|
+
let current = api;
|
|
415
|
+
for (const segment of fetchPath) {
|
|
416
|
+
current = current[segment];
|
|
417
|
+
}
|
|
418
|
+
const method = current[capturedCall.method];
|
|
419
|
+
const fetchOptions = {
|
|
420
|
+
...capturedCall.options,
|
|
421
|
+
query: opts.query,
|
|
422
|
+
params: opts.params,
|
|
423
|
+
body: opts.body,
|
|
424
|
+
signal
|
|
425
|
+
};
|
|
426
|
+
return method(fetchOptions);
|
|
427
|
+
}
|
|
428
|
+
}),
|
|
429
|
+
queryKey
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
const controller = controllerRef.current.controller;
|
|
433
|
+
controller.setPluginOptions(pluginOpts);
|
|
434
|
+
const state = (0, import_react3.useSyncExternalStore)(
|
|
435
|
+
controller.subscribe,
|
|
436
|
+
controller.getState,
|
|
437
|
+
controller.getState
|
|
438
|
+
);
|
|
439
|
+
const fetchingDirection = controller.getFetchingDirection();
|
|
440
|
+
const fetching = fetchingDirection !== null;
|
|
441
|
+
const fetchingNext = fetchingDirection === "next";
|
|
442
|
+
const fetchingPrev = fetchingDirection === "prev";
|
|
443
|
+
const loading = fetching && state.data === void 0;
|
|
444
|
+
const lifecycleRef = (0, import_react3.useRef)({
|
|
445
|
+
initialized: false,
|
|
446
|
+
prevContext: null
|
|
447
|
+
});
|
|
448
|
+
(0, import_react3.useEffect)(() => {
|
|
449
|
+
return () => {
|
|
450
|
+
controllerRef.current?.controller.unmount();
|
|
451
|
+
lifecycleRef.current.initialized = false;
|
|
452
|
+
};
|
|
453
|
+
}, []);
|
|
454
|
+
(0, import_react3.useEffect)(() => {
|
|
455
|
+
controller.mount();
|
|
456
|
+
lifecycleRef.current.initialized = true;
|
|
457
|
+
const unsubInvalidate = eventEmitter.on(
|
|
458
|
+
"invalidate",
|
|
459
|
+
(invalidatedTags) => {
|
|
460
|
+
const hasMatch = invalidatedTags.some(
|
|
461
|
+
(tag) => resolvedTags.includes(tag)
|
|
462
|
+
);
|
|
463
|
+
if (hasMatch) {
|
|
464
|
+
controller.refetch();
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
return () => {
|
|
469
|
+
unsubInvalidate();
|
|
470
|
+
};
|
|
471
|
+
}, []);
|
|
472
|
+
(0, import_react3.useEffect)(() => {
|
|
473
|
+
if (!lifecycleRef.current.initialized) return;
|
|
474
|
+
if (enabled) {
|
|
475
|
+
const currentState = controller.getState();
|
|
476
|
+
const isFetching = controller.getFetchingDirection() !== null;
|
|
477
|
+
if (currentState.data === void 0 && !isFetching) {
|
|
478
|
+
controller.fetchNext();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}, [enabled]);
|
|
482
|
+
(0, import_react3.useEffect)(() => {
|
|
483
|
+
if (!enabled || !lifecycleRef.current.initialized) return;
|
|
484
|
+
const prevContext = controller.getContext();
|
|
485
|
+
controller.update(prevContext);
|
|
486
|
+
}, [JSON.stringify(pluginOpts)]);
|
|
487
|
+
const result = {
|
|
488
|
+
data: state.data,
|
|
489
|
+
allResponses: state.allResponses,
|
|
490
|
+
loading,
|
|
491
|
+
fetching,
|
|
492
|
+
fetchingNext,
|
|
493
|
+
fetchingPrev,
|
|
494
|
+
canFetchNext: state.canFetchNext,
|
|
495
|
+
canFetchPrev: state.canFetchPrev,
|
|
496
|
+
fetchNext: controller.fetchNext,
|
|
497
|
+
fetchPrev: controller.fetchPrev,
|
|
498
|
+
refetch: controller.refetch,
|
|
499
|
+
abort: controller.abort,
|
|
500
|
+
error: state.error
|
|
501
|
+
};
|
|
502
|
+
return result;
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// src/createReactSpoosh/index.ts
|
|
507
|
+
function createReactSpoosh(instance) {
|
|
508
|
+
const { api, stateManager, eventEmitter, pluginExecutor } = instance;
|
|
509
|
+
const useRead = createUseRead({
|
|
510
|
+
api,
|
|
511
|
+
stateManager,
|
|
512
|
+
eventEmitter,
|
|
513
|
+
pluginExecutor
|
|
514
|
+
});
|
|
515
|
+
const useWrite = createUseWrite({
|
|
516
|
+
api,
|
|
517
|
+
stateManager,
|
|
518
|
+
eventEmitter,
|
|
519
|
+
pluginExecutor
|
|
520
|
+
});
|
|
521
|
+
const useInfiniteRead = createUseInfiniteRead({
|
|
522
|
+
api,
|
|
523
|
+
stateManager,
|
|
524
|
+
eventEmitter,
|
|
525
|
+
pluginExecutor
|
|
526
|
+
});
|
|
527
|
+
const instanceApiContext = {
|
|
528
|
+
api,
|
|
529
|
+
stateManager,
|
|
530
|
+
eventEmitter,
|
|
531
|
+
pluginExecutor
|
|
532
|
+
};
|
|
533
|
+
const plugins = pluginExecutor.getPlugins();
|
|
534
|
+
const instanceApis = plugins.reduce(
|
|
535
|
+
(acc, plugin) => {
|
|
536
|
+
if (plugin.instanceApi) {
|
|
537
|
+
return { ...acc, ...plugin.instanceApi(instanceApiContext) };
|
|
538
|
+
}
|
|
539
|
+
return acc;
|
|
540
|
+
},
|
|
541
|
+
{}
|
|
542
|
+
);
|
|
543
|
+
return {
|
|
544
|
+
useRead,
|
|
545
|
+
useWrite,
|
|
546
|
+
useInfiniteRead,
|
|
547
|
+
...instanceApis
|
|
548
|
+
};
|
|
549
|
+
}
|