@aiao/rxdb-react 0.0.10 → 0.0.12
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/dist/hooks.d.ts +8 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +112 -49
- package/dist/useInfiniteScroll.d.ts +29 -0
- package/dist/useInfiniteScroll.d.ts.map +1 -0
- package/package.json +7 -3
- package/src/hooks.spec.ts +26 -0
- package/src/hooks.ts +12 -0
- package/src/index.ts +1 -0
- package/src/useInfiniteScroll.spec.ts +159 -0
- package/src/useInfiniteScroll.ts +189 -0
- package/vite.config.mts +1 -0
package/dist/hooks.d.ts
CHANGED
|
@@ -69,6 +69,14 @@ export declare const useFindOneOrFail: <T extends EntityType>(EntityType: T, opt
|
|
|
69
69
|
* ```
|
|
70
70
|
*/
|
|
71
71
|
export declare const useFind: <T extends EntityType>(EntityType: T, options: UseOptions<EntityStaticType<T, "findOptions">>) => RxDBResource<InstanceType<T>[]>;
|
|
72
|
+
/**
|
|
73
|
+
* Find entities using cursor-based pagination
|
|
74
|
+
*
|
|
75
|
+
* @param EntityType The entity class
|
|
76
|
+
* @param options Cursor pagination options (where, orderBy, limit, after, before)
|
|
77
|
+
* @returns A resource object containing an array of entities
|
|
78
|
+
*/
|
|
79
|
+
export declare const useFindByCursor: <T extends EntityType>(EntityType: T, options: UseOptions<EntityStaticType<T, "findByCursorOptions">>) => RxDBResource<InstanceType<T>[]>;
|
|
72
80
|
/**
|
|
73
81
|
* Find all entities
|
|
74
82
|
*
|
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,72 +1,135 @@
|
|
|
1
|
-
import { isFunction as
|
|
2
|
-
import { useState as
|
|
3
|
-
import { jsx as
|
|
4
|
-
const s = (r, e,
|
|
5
|
-
const [D,
|
|
6
|
-
return
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
if (!
|
|
10
|
-
const
|
|
1
|
+
import { isFunction as H, cloneDeep as J } from "@aiao/utils";
|
|
2
|
+
import { useState as a, useRef as f, useMemo as P, useEffect as N, createContext as Q, useContext as U, useCallback as m } from "react";
|
|
3
|
+
import { jsx as W } from "react/jsx-runtime";
|
|
4
|
+
const s = (r, e, c, g) => {
|
|
5
|
+
const [D, p] = a(c), [y, l] = a(void 0), [B, d] = a(!0), [u, C] = a(void 0), [I, b] = a(!1), o = f(void 0), i = f(!0), M = P(() => H(g) ? g() : g, [g]);
|
|
6
|
+
return N(() => {
|
|
7
|
+
i.current = !0, o.current && (o.current.unsubscribe(), o.current = void 0);
|
|
8
|
+
const h = r[e];
|
|
9
|
+
if (!h || typeof h != "function") {
|
|
10
|
+
const t = new Error(`Method "${String(e)}" not found on EntityType`);
|
|
11
11
|
Promise.resolve().then(() => {
|
|
12
|
-
|
|
12
|
+
i.current && (l(t), d(!1));
|
|
13
13
|
});
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
try {
|
|
17
|
-
|
|
18
|
-
next: (
|
|
19
|
-
|
|
17
|
+
o.current = h(M).subscribe({
|
|
18
|
+
next: (t) => {
|
|
19
|
+
i.current && (d(!1), b(!0), l(void 0), Array.isArray(t) ? C(t.length === 0) : C(t == null), p(t));
|
|
20
20
|
},
|
|
21
|
-
error: (
|
|
22
|
-
|
|
21
|
+
error: (t) => {
|
|
22
|
+
i.current && (d(!1), b(!1), l(t), console.error(`RxDB query error in ${String(e)}:`, t));
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
} catch (
|
|
26
|
-
const
|
|
25
|
+
} catch (t) {
|
|
26
|
+
const x = t instanceof Error ? t : new Error(String(t));
|
|
27
27
|
Promise.resolve().then(() => {
|
|
28
|
-
|
|
28
|
+
i.current && (d(!1), l(x));
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
return () => {
|
|
32
|
-
|
|
32
|
+
i.current = !1, o.current && (o.current.unsubscribe(), o.current = void 0);
|
|
33
33
|
};
|
|
34
|
-
}, [r, e,
|
|
34
|
+
}, [r, e, M]), {
|
|
35
35
|
value: D,
|
|
36
|
-
error:
|
|
37
|
-
isLoading:
|
|
38
|
-
isEmpty:
|
|
39
|
-
hasValue:
|
|
36
|
+
error: y,
|
|
37
|
+
isLoading: B,
|
|
38
|
+
isEmpty: u,
|
|
39
|
+
hasValue: I
|
|
40
40
|
};
|
|
41
|
-
},
|
|
42
|
-
function
|
|
43
|
-
const r =
|
|
41
|
+
}, re = (r, e) => s(r, "get", void 0, e), te = (r, e) => s(r, "findOne", void 0, e), ne = (r, e) => s(r, "findOneOrFail", void 0, e), se = (r, e) => s(r, "find", [], e), oe = (r, e) => s(r, "findByCursor", [], e), ce = (r, e) => s(r, "findAll", [], e), ue = (r, e) => s(r, "count", 0, e), ie = (r, e) => s(r, "findDescendants", [], e), fe = (r, e) => s(r, "countDescendants", 0, e), ae = (r, e) => s(r, "findAncestors", [], e), le = (r, e) => s(r, "countAncestors", 0, e), de = (r, e) => s(r, "findNeighbors", [], e), ge = (r, e) => s(r, "countNeighbors", 0, e), he = (r, e) => s(r, "findPaths", [], e);
|
|
42
|
+
function X() {
|
|
43
|
+
const r = Q(void 0);
|
|
44
44
|
return {
|
|
45
45
|
useRxDB: ((e) => {
|
|
46
|
-
const
|
|
46
|
+
const c = U(r);
|
|
47
47
|
if (e !== void 0) return e;
|
|
48
|
-
if (!
|
|
49
|
-
return
|
|
48
|
+
if (!c) throw new Error("No RxDB instance found, use RxDBProvider to provide one");
|
|
49
|
+
return c;
|
|
50
50
|
}),
|
|
51
|
-
RxDBProvider: ({ children: e, db:
|
|
51
|
+
RxDBProvider: ({ children: e, db: c }) => /* @__PURE__ */ W(r.Provider, { value: c, children: e })
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const { RxDBProvider: ve, useRxDB: Y } = X();
|
|
55
|
+
function pe(r, e) {
|
|
56
|
+
const c = Y(), [g, D] = a([]), p = f([]), y = m(
|
|
57
|
+
(n) => {
|
|
58
|
+
const F = typeof n == "function" ? n(p.current) : n;
|
|
59
|
+
p.current = F, D(F);
|
|
60
|
+
},
|
|
61
|
+
[]
|
|
62
|
+
), [l, B] = a(!1), d = f(!1), u = m((n) => {
|
|
63
|
+
d.current = n, B(n);
|
|
64
|
+
}, []), [C, I] = a(!0), b = f(!0), o = m((n) => {
|
|
65
|
+
b.current = n, I(n);
|
|
66
|
+
}, []), [i, M] = a(!1), h = f(!1), t = f(!1), x = f([]), O = P(() => H(e) ? e() : e, [e]), E = P(() => JSON.stringify(O), [O]), A = P(() => g.flat(), [g]), V = P(
|
|
67
|
+
() => A.length === 0 && !l && i,
|
|
68
|
+
[A.length, l, i]
|
|
69
|
+
), R = m(() => {
|
|
70
|
+
x.current.forEach((n) => n.unsubscribe()), x.current = [];
|
|
71
|
+
}, []), v = m(() => {
|
|
72
|
+
if (d.current || !b.current || !t.current) return;
|
|
73
|
+
u(!0);
|
|
74
|
+
const n = p.current, F = n.length, S = n.length > 0 ? n[n.length - 1] : void 0, q = S && S.length > 0 ? S[S.length - 1] : void 0, L = J(O);
|
|
75
|
+
q && (L.after = q);
|
|
76
|
+
const K = c.entityManager.getRepository(r).findByCursor(L).subscribe({
|
|
77
|
+
next: (z) => {
|
|
78
|
+
if (!t.current) return;
|
|
79
|
+
y((j) => {
|
|
80
|
+
const G = [...j];
|
|
81
|
+
return G[F] = z, G;
|
|
82
|
+
});
|
|
83
|
+
const $ = L.limit || 100;
|
|
84
|
+
z.length < $ && o(!1), u(!1);
|
|
85
|
+
},
|
|
86
|
+
error: () => {
|
|
87
|
+
t.current && u(!1);
|
|
88
|
+
},
|
|
89
|
+
complete: () => {
|
|
90
|
+
t.current && u(!1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
x.current.push(K);
|
|
94
|
+
}, [r, O, c, o, u, y]), k = m(() => {
|
|
95
|
+
if (!t.current) {
|
|
96
|
+
console.warn("Cannot refresh: component is unmounted");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
R(), y([]), u(!1), o(!0), v();
|
|
100
|
+
}, [R, v, o, u, y]);
|
|
101
|
+
N(() => (t.current = !0, h.current || (h.current = !0, M(!0), v()), () => {
|
|
102
|
+
t.current = !1, R();
|
|
103
|
+
}), [R, v]);
|
|
104
|
+
const w = f(E);
|
|
105
|
+
return N(() => {
|
|
106
|
+
h.current && w.current !== E && (w.current = E, R(), p.current = [], d.current = !1, b.current = !0, D([]), B(!1), I(!0), v());
|
|
107
|
+
}, [E, R, v]), {
|
|
108
|
+
value: A,
|
|
109
|
+
isEmpty: V,
|
|
110
|
+
isLoading: l,
|
|
111
|
+
hasMore: C,
|
|
112
|
+
loadMore: v,
|
|
113
|
+
refresh: k
|
|
52
114
|
};
|
|
53
115
|
}
|
|
54
|
-
const { RxDBProvider: K, useRxDB: T } = C();
|
|
55
116
|
export {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
117
|
+
ve as RxDBProvider,
|
|
118
|
+
X as makeRxDBProvider,
|
|
119
|
+
ue as useCount,
|
|
120
|
+
le as useCountAncestors,
|
|
121
|
+
fe as useCountDescendants,
|
|
122
|
+
ge as useCountNeighbors,
|
|
123
|
+
se as useFind,
|
|
124
|
+
ce as useFindAll,
|
|
125
|
+
ae as useFindAncestors,
|
|
126
|
+
oe as useFindByCursor,
|
|
127
|
+
ie as useFindDescendants,
|
|
128
|
+
te as useFindOne,
|
|
129
|
+
ne as useFindOneOrFail,
|
|
130
|
+
re as useGet,
|
|
131
|
+
de as useGraphNeighbors,
|
|
132
|
+
he as useGraphPaths,
|
|
133
|
+
pe as useInfiniteScroll,
|
|
134
|
+
Y as useRxDB
|
|
72
135
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EntityStaticType, EntityType } from '../../rxdb/src/index.ts';
|
|
2
|
+
type UseOptions<T> = T | (() => T);
|
|
3
|
+
export interface InfiniteScrollResource<T> {
|
|
4
|
+
value: T[];
|
|
5
|
+
isEmpty: boolean;
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
hasMore: boolean;
|
|
8
|
+
loadMore: () => void;
|
|
9
|
+
refresh: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Infinite scroll hook for cursor-based pagination
|
|
13
|
+
*
|
|
14
|
+
* @param EntityType The entity class
|
|
15
|
+
* @param options Query options with cursor pagination
|
|
16
|
+
* @returns A resource object with infinite scroll controls
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const resource = useInfiniteScroll(Todo, {
|
|
21
|
+
* where: { completed: false },
|
|
22
|
+
* orderBy: [{ field: 'createdAt', sort: 'desc' }],
|
|
23
|
+
* limit: 50
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function useInfiniteScroll<T extends EntityType>(EntityType: T, options: UseOptions<EntityStaticType<T, 'findByCursorOptions'>>): InfiniteScrollResource<InstanceType<T>>;
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=useInfiniteScroll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useInfiniteScroll.d.ts","sourceRoot":"","sources":["../src/useInfiniteScroll.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAK1D,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAEnC,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,UAAU,EACpD,UAAU,EAAE,CAAC,EACb,OAAO,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC,GAC9D,sBAAsB,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAyJzC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiao/rxdb-react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -15,8 +15,12 @@
|
|
|
15
15
|
}
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@aiao/
|
|
19
|
-
"@aiao/
|
|
18
|
+
"@aiao/utils": "0.0.12",
|
|
19
|
+
"@aiao/rxdb": "0.0.12"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"react": ">=19.0.0",
|
|
23
|
+
"rxjs": "^7.8.0"
|
|
20
24
|
},
|
|
21
25
|
"nx": {
|
|
22
26
|
"name": "rxdb-react",
|
package/src/hooks.spec.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
useFind,
|
|
9
9
|
useFindAll,
|
|
10
10
|
useFindAncestors,
|
|
11
|
+
useFindByCursor,
|
|
11
12
|
useFindDescendants,
|
|
12
13
|
useFindOne,
|
|
13
14
|
useFindOneOrFail,
|
|
@@ -56,6 +57,7 @@ class MockEntity {
|
|
|
56
57
|
static countAncestors = vi.fn();
|
|
57
58
|
static findNeighbors = vi.fn();
|
|
58
59
|
static countNeighbors = vi.fn();
|
|
60
|
+
static findByCursor = vi.fn();
|
|
59
61
|
static findPaths = vi.fn();
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -133,6 +135,30 @@ describe('useFind', () => {
|
|
|
133
135
|
});
|
|
134
136
|
});
|
|
135
137
|
|
|
138
|
+
describe('useFindByCursor', () => {
|
|
139
|
+
beforeEach(() => {
|
|
140
|
+
vi.clearAllMocks();
|
|
141
|
+
mockState = [];
|
|
142
|
+
mockStateIndex = 0;
|
|
143
|
+
mockEffectCleanups = [];
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should call findByCursor method', () => {
|
|
147
|
+
const mockData = [
|
|
148
|
+
{ id: '1', name: 'User1' },
|
|
149
|
+
{ id: '2', name: 'User2' }
|
|
150
|
+
];
|
|
151
|
+
MockEntity.findByCursor.mockReturnValue(of(mockData));
|
|
152
|
+
|
|
153
|
+
useFindByCursor(MockEntity as any, {
|
|
154
|
+
where: {},
|
|
155
|
+
orderBy: [{ field: 'id', sort: 'ASC' }]
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect(MockEntity.findByCursor).toHaveBeenCalled();
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
|
|
136
162
|
describe('Hook cleanup', () => {
|
|
137
163
|
beforeEach(() => {
|
|
138
164
|
vi.clearAllMocks();
|
package/src/hooks.ts
CHANGED
|
@@ -205,6 +205,18 @@ export const useFind = <T extends EntityType>(
|
|
|
205
205
|
options: UseOptions<EntityStaticType<T, 'findOptions'>>
|
|
206
206
|
): RxDBResource<InstanceType<T>[]> => useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'find', [], options);
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Find entities using cursor-based pagination
|
|
210
|
+
*
|
|
211
|
+
* @param EntityType The entity class
|
|
212
|
+
* @param options Cursor pagination options (where, orderBy, limit, after, before)
|
|
213
|
+
* @returns A resource object containing an array of entities
|
|
214
|
+
*/
|
|
215
|
+
export const useFindByCursor = <T extends EntityType>(
|
|
216
|
+
EntityType: T,
|
|
217
|
+
options: UseOptions<EntityStaticType<T, 'findByCursorOptions'>>
|
|
218
|
+
): RxDBResource<InstanceType<T>[]> => useRepositoryQuery<T, InstanceType<T>[]>(EntityType, 'findByCursor', [], options);
|
|
219
|
+
|
|
208
220
|
/**
|
|
209
221
|
* Find all entities
|
|
210
222
|
*
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { of, throwError } from 'rxjs';
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
|
|
4
|
+
// Import after mocks are set up
|
|
5
|
+
import { useInfiniteScroll } from './useInfiniteScroll';
|
|
6
|
+
|
|
7
|
+
// Mock findByCursor + repository
|
|
8
|
+
const mockFindByCursor = vi.fn();
|
|
9
|
+
const mockGetRepository = vi.fn(() => ({ findByCursor: mockFindByCursor }));
|
|
10
|
+
|
|
11
|
+
// Mock useRxDB before importing useInfiniteScroll
|
|
12
|
+
vi.mock('./rxdb-react', () => ({
|
|
13
|
+
useRxDB: () => ({ entityManager: { getRepository: mockGetRepository } })
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
// Mock React hooks
|
|
17
|
+
let mockState: any[] = [];
|
|
18
|
+
let mockStateIndex = 0;
|
|
19
|
+
let mockEffectCleanups: Array<() => void> = [];
|
|
20
|
+
|
|
21
|
+
vi.mock('react', () => ({
|
|
22
|
+
useState: (initial: any) => {
|
|
23
|
+
const index = mockStateIndex++;
|
|
24
|
+
if (mockState[index] === undefined) {
|
|
25
|
+
mockState[index] = typeof initial === 'function' ? initial() : initial;
|
|
26
|
+
}
|
|
27
|
+
const setState = (newValue: any) => {
|
|
28
|
+
mockState[index] = typeof newValue === 'function' ? newValue(mockState[index]) : newValue;
|
|
29
|
+
};
|
|
30
|
+
return [mockState[index], setState];
|
|
31
|
+
},
|
|
32
|
+
useEffect: (effect: () => any) => {
|
|
33
|
+
const cleanup = effect();
|
|
34
|
+
if (cleanup) mockEffectCleanups.push(cleanup);
|
|
35
|
+
},
|
|
36
|
+
useRef: (initial: any) => ({ current: initial }),
|
|
37
|
+
useMemo: (fn: () => any) => fn(),
|
|
38
|
+
useCallback: (fn: any) => fn
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
class MockEntity {
|
|
42
|
+
id!: string;
|
|
43
|
+
name!: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const defaultOptions = {
|
|
47
|
+
where: {},
|
|
48
|
+
orderBy: [{ field: 'id', sort: 'ASC' }],
|
|
49
|
+
limit: 10
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
describe('useInfiniteScroll (React)', () => {
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
vi.clearAllMocks();
|
|
55
|
+
mockState = [];
|
|
56
|
+
mockStateIndex = 0;
|
|
57
|
+
mockEffectCleanups = [];
|
|
58
|
+
mockGetRepository.mockReturnValue({ findByCursor: mockFindByCursor });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
mockEffectCleanups.forEach(cleanup => cleanup());
|
|
63
|
+
mockEffectCleanups = [];
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should export useInfiniteScroll function', () => {
|
|
67
|
+
expect(useInfiniteScroll).toBeDefined();
|
|
68
|
+
expect(typeof useInfiniteScroll).toBe('function');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should return InfiniteScrollResource structure', () => {
|
|
72
|
+
mockFindByCursor.mockReturnValue(of([]));
|
|
73
|
+
|
|
74
|
+
const resource = useInfiniteScroll(MockEntity as any, defaultOptions);
|
|
75
|
+
|
|
76
|
+
expect(resource.value).toBeDefined();
|
|
77
|
+
expect(typeof resource.isEmpty).toBe('boolean');
|
|
78
|
+
expect(typeof resource.isLoading).toBe('boolean');
|
|
79
|
+
expect(typeof resource.hasMore).toBe('boolean');
|
|
80
|
+
expect(typeof resource.loadMore).toBe('function');
|
|
81
|
+
expect(typeof resource.refresh).toBe('function');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should call findByCursor on initial load', () => {
|
|
85
|
+
mockFindByCursor.mockReturnValue(of([{ id: '1', name: 'a' }]));
|
|
86
|
+
|
|
87
|
+
useInfiniteScroll(MockEntity as any, defaultOptions);
|
|
88
|
+
|
|
89
|
+
expect(mockGetRepository).toHaveBeenCalledWith(MockEntity);
|
|
90
|
+
expect(mockFindByCursor).toHaveBeenCalled();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should return value from subscription', () => {
|
|
94
|
+
const items = [
|
|
95
|
+
{ id: '1', name: 'a' },
|
|
96
|
+
{ id: '2', name: 'b' }
|
|
97
|
+
];
|
|
98
|
+
mockFindByCursor.mockReturnValue(of(items));
|
|
99
|
+
|
|
100
|
+
const resource = useInfiniteScroll(MockEntity as any, { ...defaultOptions, limit: 10 });
|
|
101
|
+
|
|
102
|
+
expect(resource).toBeDefined();
|
|
103
|
+
expect(mockFindByCursor).toHaveBeenCalledTimes(1);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should support function options', () => {
|
|
107
|
+
mockFindByCursor.mockReturnValue(of([]));
|
|
108
|
+
|
|
109
|
+
const resource = useInfiniteScroll(MockEntity as any, () => ({
|
|
110
|
+
where: { status: 'active' },
|
|
111
|
+
orderBy: [{ field: 'id', sort: 'ASC' }],
|
|
112
|
+
limit: 20
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
expect(resource).toBeDefined();
|
|
116
|
+
expect(mockFindByCursor).toHaveBeenCalled();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should clean up subscriptions on unmount', () => {
|
|
120
|
+
const unsubscribeSpy = vi.fn();
|
|
121
|
+
mockFindByCursor.mockReturnValue({
|
|
122
|
+
subscribe: (observer: any) => {
|
|
123
|
+
observer.next([]);
|
|
124
|
+
return { unsubscribe: unsubscribeSpy };
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
useInfiniteScroll(MockEntity as any, defaultOptions);
|
|
129
|
+
|
|
130
|
+
mockEffectCleanups.forEach(cleanup => cleanup());
|
|
131
|
+
mockEffectCleanups = [];
|
|
132
|
+
|
|
133
|
+
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should handle subscription errors gracefully', () => {
|
|
137
|
+
mockFindByCursor.mockReturnValue(throwError(() => new Error('fail')));
|
|
138
|
+
|
|
139
|
+
expect(() => {
|
|
140
|
+
useInfiniteScroll(MockEntity as any, defaultOptions);
|
|
141
|
+
}).not.toThrow();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should expose refresh function', () => {
|
|
145
|
+
mockFindByCursor.mockReturnValue(of([]));
|
|
146
|
+
|
|
147
|
+
const resource = useInfiniteScroll(MockEntity as any, defaultOptions);
|
|
148
|
+
|
|
149
|
+
expect(typeof resource.refresh).toBe('function');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should expose loadMore function', () => {
|
|
153
|
+
mockFindByCursor.mockReturnValue(of([]));
|
|
154
|
+
|
|
155
|
+
const resource = useInfiniteScroll(MockEntity as any, defaultOptions);
|
|
156
|
+
|
|
157
|
+
expect(typeof resource.loadMore).toBe('function');
|
|
158
|
+
});
|
|
159
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { EntityStaticType, EntityType } from '@aiao/rxdb';
|
|
2
|
+
import { cloneDeep, isFunction } from '@aiao/utils';
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { useRxDB } from './rxdb-react';
|
|
5
|
+
|
|
6
|
+
type UseOptions<T> = T | (() => T);
|
|
7
|
+
|
|
8
|
+
export interface InfiniteScrollResource<T> {
|
|
9
|
+
value: T[];
|
|
10
|
+
isEmpty: boolean;
|
|
11
|
+
isLoading: boolean;
|
|
12
|
+
hasMore: boolean;
|
|
13
|
+
loadMore: () => void;
|
|
14
|
+
refresh: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Infinite scroll hook for cursor-based pagination
|
|
19
|
+
*
|
|
20
|
+
* @param EntityType The entity class
|
|
21
|
+
* @param options Query options with cursor pagination
|
|
22
|
+
* @returns A resource object with infinite scroll controls
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const resource = useInfiniteScroll(Todo, {
|
|
27
|
+
* where: { completed: false },
|
|
28
|
+
* orderBy: [{ field: 'createdAt', sort: 'desc' }],
|
|
29
|
+
* limit: 50
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function useInfiniteScroll<T extends EntityType>(
|
|
34
|
+
EntityType: T,
|
|
35
|
+
options: UseOptions<EntityStaticType<T, 'findByCursorOptions'>>
|
|
36
|
+
): InfiniteScrollResource<InstanceType<T>> {
|
|
37
|
+
const rxdb = useRxDB();
|
|
38
|
+
|
|
39
|
+
const [pages, setPages] = useState<InstanceType<T>[][]>([]);
|
|
40
|
+
const pagesRef = useRef<InstanceType<T>[][]>([]);
|
|
41
|
+
const setPagesState = useCallback(
|
|
42
|
+
(next: InstanceType<T>[][] | ((prev: InstanceType<T>[][]) => InstanceType<T>[][])) => {
|
|
43
|
+
const resolved =
|
|
44
|
+
typeof next === 'function' ?
|
|
45
|
+
(next as (prev: InstanceType<T>[][]) => InstanceType<T>[][])(pagesRef.current)
|
|
46
|
+
: next;
|
|
47
|
+
pagesRef.current = resolved;
|
|
48
|
+
setPages(resolved);
|
|
49
|
+
},
|
|
50
|
+
[]
|
|
51
|
+
);
|
|
52
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
53
|
+
const isLoadingRef = useRef(false);
|
|
54
|
+
const setIsLoadingState = useCallback((value: boolean) => {
|
|
55
|
+
isLoadingRef.current = value;
|
|
56
|
+
setIsLoading(value);
|
|
57
|
+
}, []);
|
|
58
|
+
const [hasMore, setHasMore] = useState(true);
|
|
59
|
+
const hasMoreRef = useRef(true);
|
|
60
|
+
const setHasMoreState = useCallback((value: boolean) => {
|
|
61
|
+
hasMoreRef.current = value;
|
|
62
|
+
setHasMore(value);
|
|
63
|
+
}, []);
|
|
64
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
65
|
+
const isInitializedRef = useRef(false);
|
|
66
|
+
const isMounted = useRef(false);
|
|
67
|
+
const subscriptions = useRef<{ unsubscribe: () => void }[]>([]);
|
|
68
|
+
|
|
69
|
+
const resolvedOptions = useMemo(() => {
|
|
70
|
+
return isFunction(options) ? options() : options;
|
|
71
|
+
}, [options]);
|
|
72
|
+
|
|
73
|
+
const optionsKey = useMemo(() => JSON.stringify(resolvedOptions), [resolvedOptions]);
|
|
74
|
+
|
|
75
|
+
const allItems = useMemo(() => pages.flat(), [pages]);
|
|
76
|
+
const isEmpty = useMemo(
|
|
77
|
+
() => allItems.length === 0 && !isLoading && isInitialized,
|
|
78
|
+
[allItems.length, isLoading, isInitialized]
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const clearSubscriptions = useCallback(() => {
|
|
82
|
+
subscriptions.current.forEach(sub => sub.unsubscribe());
|
|
83
|
+
subscriptions.current = [];
|
|
84
|
+
}, []);
|
|
85
|
+
|
|
86
|
+
const loadMore = useCallback(() => {
|
|
87
|
+
if (isLoadingRef.current || !hasMoreRef.current || !isMounted.current) return;
|
|
88
|
+
|
|
89
|
+
setIsLoadingState(true);
|
|
90
|
+
|
|
91
|
+
const currentPages = pagesRef.current;
|
|
92
|
+
const currentIndex = currentPages.length;
|
|
93
|
+
const lastPage = currentPages.length > 0 ? currentPages[currentPages.length - 1] : undefined;
|
|
94
|
+
const lastEntity = lastPage && lastPage.length > 0 ? lastPage[lastPage.length - 1] : undefined;
|
|
95
|
+
|
|
96
|
+
const queryOptions = cloneDeep(resolvedOptions);
|
|
97
|
+
if (lastEntity) {
|
|
98
|
+
queryOptions.after = lastEntity;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const repository = rxdb.entityManager.getRepository(EntityType);
|
|
102
|
+
|
|
103
|
+
const subscription = repository.findByCursor(queryOptions).subscribe({
|
|
104
|
+
next: (result: InstanceType<T>[]) => {
|
|
105
|
+
if (!isMounted.current) return;
|
|
106
|
+
|
|
107
|
+
setPagesState(prev => {
|
|
108
|
+
const updated = [...prev];
|
|
109
|
+
updated[currentIndex] = result;
|
|
110
|
+
return updated;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const limit = queryOptions.limit || 100;
|
|
114
|
+
if (result.length < limit) {
|
|
115
|
+
setHasMoreState(false);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
setIsLoadingState(false);
|
|
119
|
+
},
|
|
120
|
+
error: () => {
|
|
121
|
+
if (!isMounted.current) return;
|
|
122
|
+
setIsLoadingState(false);
|
|
123
|
+
},
|
|
124
|
+
complete: () => {
|
|
125
|
+
if (!isMounted.current) return;
|
|
126
|
+
setIsLoadingState(false);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
subscriptions.current.push(subscription);
|
|
131
|
+
}, [EntityType, resolvedOptions, rxdb, setHasMoreState, setIsLoadingState, setPagesState]);
|
|
132
|
+
|
|
133
|
+
const refresh = useCallback(() => {
|
|
134
|
+
if (!isMounted.current) {
|
|
135
|
+
console.warn('Cannot refresh: component is unmounted');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
clearSubscriptions();
|
|
139
|
+
setPagesState([]);
|
|
140
|
+
setIsLoadingState(false);
|
|
141
|
+
setHasMoreState(true);
|
|
142
|
+
loadMore();
|
|
143
|
+
}, [clearSubscriptions, loadMore, setHasMoreState, setIsLoadingState, setPagesState]);
|
|
144
|
+
|
|
145
|
+
// Initial load
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
isMounted.current = true;
|
|
148
|
+
if (!isInitializedRef.current) {
|
|
149
|
+
isInitializedRef.current = true;
|
|
150
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect -- 初始化标记仅在首次 mount 时设置一次
|
|
151
|
+
setIsInitialized(true);
|
|
152
|
+
|
|
153
|
+
loadMore();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return () => {
|
|
157
|
+
isMounted.current = false;
|
|
158
|
+
clearSubscriptions();
|
|
159
|
+
};
|
|
160
|
+
}, [clearSubscriptions, loadMore]);
|
|
161
|
+
|
|
162
|
+
// Reload on options change
|
|
163
|
+
const prevOptionsKey = useRef(optionsKey);
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!isInitializedRef.current) return;
|
|
166
|
+
|
|
167
|
+
if (prevOptionsKey.current !== optionsKey) {
|
|
168
|
+
prevOptionsKey.current = optionsKey;
|
|
169
|
+
clearSubscriptions();
|
|
170
|
+
pagesRef.current = [];
|
|
171
|
+
isLoadingRef.current = false;
|
|
172
|
+
hasMoreRef.current = true;
|
|
173
|
+
// eslint-disable-next-line react-hooks/set-state-in-effect
|
|
174
|
+
setPages([]);
|
|
175
|
+
setIsLoading(false);
|
|
176
|
+
setHasMore(true);
|
|
177
|
+
loadMore();
|
|
178
|
+
}
|
|
179
|
+
}, [optionsKey, clearSubscriptions, loadMore]);
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
value: allItems,
|
|
183
|
+
isEmpty,
|
|
184
|
+
isLoading,
|
|
185
|
+
hasMore,
|
|
186
|
+
loadMore,
|
|
187
|
+
refresh
|
|
188
|
+
};
|
|
189
|
+
}
|
package/vite.config.mts
CHANGED
|
@@ -73,6 +73,7 @@ export default defineConfig({
|
|
|
73
73
|
enabled: true,
|
|
74
74
|
reportsDirectory: '../../coverage/packages/rxdb-react',
|
|
75
75
|
provider: 'v8' as const,
|
|
76
|
+
reporter: ['text', 'json', 'clover', 'lcovonly'],
|
|
76
77
|
include: ['src/**/*'],
|
|
77
78
|
exclude: ['**/index.ts', '**/dist/**'],
|
|
78
79
|
all: false
|