ccstate-react 4.12.0 → 5.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/CHANGELOG.md +27 -0
- package/dist/experimental.cjs +0 -20
- package/dist/experimental.d.cts +2 -3
- package/dist/experimental.d.ts +2 -3
- package/dist/experimental.js +3 -22
- package/dist/index.cjs +54 -104
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +55 -105
- package/package.json +3 -3
- package/src/__tests__/get-and-set.test.tsx +22 -13
- package/src/__tests__/inline-atom.test.tsx +6 -61
- package/src/__tests__/loadable.test.tsx +53 -5
- package/src/__tests__/resolved.test.tsx +6 -1
- package/src/experimental.ts +1 -1
- package/src/provider.ts +1 -2
- package/src/useGet.ts +19 -25
- package/src/useInlineAtom.ts +2 -11
- package/src/useLoadable.ts +71 -51
- package/src/useResolved.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# ccstate-react
|
|
2
2
|
|
|
3
|
+
## 5.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 2fdba09: feat: provide watch method to replace sub
|
|
8
|
+
|
|
9
|
+
### Minor Changes
|
|
10
|
+
|
|
11
|
+
- 211f5b5: refactor: better promise rejection handling
|
|
12
|
+
- 52c52fd: refactor: remove defaultStore
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Updated dependencies [2fdba09]
|
|
17
|
+
- Updated dependencies [52c52fd]
|
|
18
|
+
- ccstate@5.0.0
|
|
19
|
+
|
|
20
|
+
## 4.13.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- 8f06427: refactor: avoid unhandledRejection in useLoadable
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- ccstate@4.13.0
|
|
29
|
+
|
|
3
30
|
## 4.12.0
|
|
4
31
|
|
|
5
32
|
### Minor Changes
|
package/dist/experimental.cjs
CHANGED
|
@@ -3,16 +3,6 @@
|
|
|
3
3
|
var ccstate = require('ccstate');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
|
|
6
|
-
var StoreContext = react.createContext(null);
|
|
7
|
-
StoreContext.Provider;
|
|
8
|
-
function useStore() {
|
|
9
|
-
var store = react.useContext(StoreContext);
|
|
10
|
-
if (!store) {
|
|
11
|
-
return ccstate.getDefaultStore();
|
|
12
|
-
}
|
|
13
|
-
return store;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
6
|
function useRefFactory(factory) {
|
|
17
7
|
var ref = react.useRef(null);
|
|
18
8
|
if (!ref.current) {
|
|
@@ -46,17 +36,7 @@ function useCommand() {
|
|
|
46
36
|
return ccstate.command.apply(void 0, args);
|
|
47
37
|
});
|
|
48
38
|
}
|
|
49
|
-
function useSub() {
|
|
50
|
-
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
|
51
|
-
args[_key4] = arguments[_key4];
|
|
52
|
-
}
|
|
53
|
-
var store = useStore();
|
|
54
|
-
react.useEffect(function () {
|
|
55
|
-
return store.sub.apply(store, args);
|
|
56
|
-
}, []);
|
|
57
|
-
}
|
|
58
39
|
|
|
59
40
|
exports.useCCState = useCCState;
|
|
60
41
|
exports.useCommand = useCommand;
|
|
61
42
|
exports.useComputed = useComputed;
|
|
62
|
-
exports.useSub = useSub;
|
package/dist/experimental.d.cts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { state, State, computed, Computed, command, Command
|
|
1
|
+
import { state, State, computed, Computed, command, Command } from 'ccstate';
|
|
2
2
|
|
|
3
3
|
declare function useCCState<T>(...args: Parameters<typeof state<T>>): State<T>;
|
|
4
4
|
declare function useComputed<T>(...args: Parameters<typeof computed<T>>): Computed<T>;
|
|
5
5
|
declare function useCommand<T, Args extends unknown[]>(...args: Parameters<typeof command<T, Args>>): Command<T, Args>;
|
|
6
|
-
declare function useSub(...args: Parameters<Subscribe>): void;
|
|
7
6
|
|
|
8
|
-
export { useCCState, useCommand, useComputed
|
|
7
|
+
export { useCCState, useCommand, useComputed };
|
package/dist/experimental.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { state, State, computed, Computed, command, Command
|
|
1
|
+
import { state, State, computed, Computed, command, Command } from 'ccstate';
|
|
2
2
|
|
|
3
3
|
declare function useCCState<T>(...args: Parameters<typeof state<T>>): State<T>;
|
|
4
4
|
declare function useComputed<T>(...args: Parameters<typeof computed<T>>): Computed<T>;
|
|
5
5
|
declare function useCommand<T, Args extends unknown[]>(...args: Parameters<typeof command<T, Args>>): Command<T, Args>;
|
|
6
|
-
declare function useSub(...args: Parameters<Subscribe>): void;
|
|
7
6
|
|
|
8
|
-
export { useCCState, useCommand, useComputed
|
|
7
|
+
export { useCCState, useCommand, useComputed };
|
package/dist/experimental.js
CHANGED
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
var StoreContext = createContext(null);
|
|
5
|
-
StoreContext.Provider;
|
|
6
|
-
function useStore() {
|
|
7
|
-
var store = useContext(StoreContext);
|
|
8
|
-
if (!store) {
|
|
9
|
-
return getDefaultStore();
|
|
10
|
-
}
|
|
11
|
-
return store;
|
|
12
|
-
}
|
|
1
|
+
import { state, computed, command } from 'ccstate';
|
|
2
|
+
import { useRef } from 'react';
|
|
13
3
|
|
|
14
4
|
function useRefFactory(factory) {
|
|
15
5
|
var ref = useRef(null);
|
|
@@ -44,14 +34,5 @@ function useCommand() {
|
|
|
44
34
|
return command.apply(void 0, args);
|
|
45
35
|
});
|
|
46
36
|
}
|
|
47
|
-
function useSub() {
|
|
48
|
-
for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
|
|
49
|
-
args[_key4] = arguments[_key4];
|
|
50
|
-
}
|
|
51
|
-
var store = useStore();
|
|
52
|
-
useEffect(function () {
|
|
53
|
-
return store.sub.apply(store, args);
|
|
54
|
-
}, []);
|
|
55
|
-
}
|
|
56
37
|
|
|
57
|
-
export { useCCState, useCommand, useComputed
|
|
38
|
+
export { useCCState, useCommand, useComputed };
|
package/dist/index.cjs
CHANGED
|
@@ -1,42 +1,33 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var react = require('react');
|
|
4
|
-
var ccstate = require('ccstate');
|
|
5
4
|
|
|
6
5
|
var StoreContext = react.createContext(null);
|
|
7
6
|
var StoreProvider = StoreContext.Provider;
|
|
8
7
|
function useStore() {
|
|
9
8
|
var store = react.useContext(StoreContext);
|
|
10
9
|
if (!store) {
|
|
11
|
-
|
|
10
|
+
throw new Error('useStore must be used within a StoreProvider');
|
|
12
11
|
}
|
|
13
12
|
return store;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
function
|
|
17
|
-
var silenceUnhandleRejection = _ref.silenceUnhandleRejection;
|
|
15
|
+
function useGet(atom) {
|
|
18
16
|
var store = useStore();
|
|
19
|
-
|
|
20
|
-
var
|
|
21
|
-
store.
|
|
22
|
-
|
|
17
|
+
var onChange = react.useRef(function (fn) {
|
|
18
|
+
var controller = new AbortController();
|
|
19
|
+
store.watch(function (get) {
|
|
20
|
+
get(atom);
|
|
21
|
+
fn();
|
|
22
|
+
}, {
|
|
23
|
+
signal: controller.signal
|
|
23
24
|
});
|
|
24
25
|
return function () {
|
|
25
|
-
|
|
26
|
+
controller.abort();
|
|
26
27
|
};
|
|
27
|
-
}, function () {
|
|
28
|
-
var val = store.get(atom);
|
|
29
|
-
if (val instanceof Promise && silenceUnhandleRejection) {
|
|
30
|
-
val["catch"](function () {
|
|
31
|
-
return void 0;
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
return val;
|
|
35
28
|
});
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return useGetInternal(atom, {
|
|
39
|
-
silenceUnhandleRejection: false
|
|
29
|
+
return react.useSyncExternalStore(onChange.current, function () {
|
|
30
|
+
return store.get(atom);
|
|
40
31
|
});
|
|
41
32
|
}
|
|
42
33
|
|
|
@@ -52,95 +43,54 @@ function useSet(signal) {
|
|
|
52
43
|
}, [store, signal]);
|
|
53
44
|
}
|
|
54
45
|
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return n;
|
|
59
|
-
}
|
|
60
|
-
function _arrayWithHoles(r) {
|
|
61
|
-
if (Array.isArray(r)) return r;
|
|
62
|
-
}
|
|
63
|
-
function _iterableToArrayLimit(r, l) {
|
|
64
|
-
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
65
|
-
if (null != t) {
|
|
66
|
-
var e,
|
|
67
|
-
n,
|
|
68
|
-
i,
|
|
69
|
-
u,
|
|
70
|
-
a = [],
|
|
71
|
-
f = !0,
|
|
72
|
-
o = !1;
|
|
73
|
-
try {
|
|
74
|
-
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
|
|
75
|
-
} catch (r) {
|
|
76
|
-
o = !0, n = r;
|
|
77
|
-
} finally {
|
|
78
|
-
try {
|
|
79
|
-
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
|
|
80
|
-
} finally {
|
|
81
|
-
if (o) throw n;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return a;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
function _nonIterableRest() {
|
|
88
|
-
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
89
|
-
}
|
|
90
|
-
function _slicedToArray(r, e) {
|
|
91
|
-
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
|
|
92
|
-
}
|
|
93
|
-
function _unsupportedIterableToArray(r, a) {
|
|
94
|
-
if (r) {
|
|
95
|
-
if ("string" == typeof r) return _arrayLikeToArray(r, a);
|
|
96
|
-
var t = {}.toString.call(r).slice(8, -1);
|
|
97
|
-
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function useLoadableInternal(atom, keepLastResolved) {
|
|
102
|
-
var promise = useGetInternal(atom, {
|
|
103
|
-
silenceUnhandleRejection: true
|
|
46
|
+
function useLoadableInternal(promise$, keepLastResolved) {
|
|
47
|
+
var promiseResult = react.useRef({
|
|
48
|
+
state: 'loading'
|
|
104
49
|
});
|
|
105
|
-
var
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
_useState2 = _slicedToArray(_useState, 2),
|
|
109
|
-
promiseResult = _useState2[0],
|
|
110
|
-
setPromiseResult = _useState2[1];
|
|
111
|
-
react.useEffect(function () {
|
|
112
|
-
if (!(promise instanceof Promise)) {
|
|
113
|
-
setPromiseResult({
|
|
114
|
-
state: 'hasData',
|
|
115
|
-
data: promise
|
|
116
|
-
});
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
var ctrl = new AbortController();
|
|
120
|
-
var signal = ctrl.signal;
|
|
121
|
-
if (!keepLastResolved) {
|
|
122
|
-
setPromiseResult({
|
|
123
|
-
state: 'loading'
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
promise.then(function (ret) {
|
|
127
|
-
if (signal.aborted) return;
|
|
128
|
-
setPromiseResult({
|
|
129
|
-
state: 'hasData',
|
|
130
|
-
data: ret
|
|
131
|
-
});
|
|
132
|
-
}, function (error) {
|
|
50
|
+
var store = useStore();
|
|
51
|
+
var subStore = react.useCallback(function (fn) {
|
|
52
|
+
function updateResult(result, signal) {
|
|
133
53
|
if (signal.aborted) return;
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
54
|
+
promiseResult.current = result;
|
|
55
|
+
fn();
|
|
56
|
+
}
|
|
57
|
+
var controller = new AbortController();
|
|
58
|
+
store.watch(function (get, _ref) {
|
|
59
|
+
var signal = _ref.signal;
|
|
60
|
+
var promise = get(promise$);
|
|
61
|
+
if (!(promise instanceof Promise)) {
|
|
62
|
+
updateResult({
|
|
63
|
+
state: 'hasData',
|
|
64
|
+
data: promise
|
|
65
|
+
}, signal);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!keepLastResolved) {
|
|
69
|
+
updateResult({
|
|
70
|
+
state: 'loading'
|
|
71
|
+
}, signal);
|
|
72
|
+
}
|
|
73
|
+
promise.then(function (ret) {
|
|
74
|
+
updateResult({
|
|
75
|
+
state: 'hasData',
|
|
76
|
+
data: ret
|
|
77
|
+
}, signal);
|
|
78
|
+
}, function (error) {
|
|
79
|
+
updateResult({
|
|
80
|
+
state: 'hasError',
|
|
81
|
+
error: error
|
|
82
|
+
}, signal);
|
|
137
83
|
});
|
|
84
|
+
}, {
|
|
85
|
+
signal: controller.signal
|
|
138
86
|
});
|
|
139
87
|
return function () {
|
|
140
|
-
|
|
88
|
+
controller.abort();
|
|
141
89
|
};
|
|
142
|
-
}, [promise]);
|
|
143
|
-
return
|
|
90
|
+
}, [store, promise$]);
|
|
91
|
+
return react.useSyncExternalStore(subStore, function () {
|
|
92
|
+
return promiseResult.current;
|
|
93
|
+
});
|
|
144
94
|
}
|
|
145
95
|
function useLoadable(atom) {
|
|
146
96
|
return useLoadableInternal(atom, false);
|
package/dist/index.d.cts
CHANGED
|
@@ -15,13 +15,13 @@ type Loadable<T> = {
|
|
|
15
15
|
state: 'loading';
|
|
16
16
|
} | {
|
|
17
17
|
state: 'hasData';
|
|
18
|
-
data: T
|
|
18
|
+
data: Awaited<T>;
|
|
19
19
|
} | {
|
|
20
20
|
state: 'hasError';
|
|
21
21
|
error: unknown;
|
|
22
22
|
};
|
|
23
|
-
declare function useLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<
|
|
24
|
-
declare function useLastLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<
|
|
23
|
+
declare function useLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<T>;
|
|
24
|
+
declare function useLastLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<T>;
|
|
25
25
|
|
|
26
26
|
declare const StoreProvider: react.Provider<Store | null>;
|
|
27
27
|
|
package/dist/index.d.ts
CHANGED
|
@@ -15,13 +15,13 @@ type Loadable<T> = {
|
|
|
15
15
|
state: 'loading';
|
|
16
16
|
} | {
|
|
17
17
|
state: 'hasData';
|
|
18
|
-
data: T
|
|
18
|
+
data: Awaited<T>;
|
|
19
19
|
} | {
|
|
20
20
|
state: 'hasError';
|
|
21
21
|
error: unknown;
|
|
22
22
|
};
|
|
23
|
-
declare function useLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<
|
|
24
|
-
declare function useLastLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<
|
|
23
|
+
declare function useLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<T>;
|
|
24
|
+
declare function useLastLoadable<T>(atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>): Loadable<T>;
|
|
25
25
|
|
|
26
26
|
declare const StoreProvider: react.Provider<Store | null>;
|
|
27
27
|
|
package/dist/index.js
CHANGED
|
@@ -1,40 +1,31 @@
|
|
|
1
|
-
import { createContext, useContext, useSyncExternalStore, useCallback
|
|
2
|
-
import { getDefaultStore, command } from 'ccstate';
|
|
1
|
+
import { createContext, useContext, useRef, useSyncExternalStore, useCallback } from 'react';
|
|
3
2
|
|
|
4
3
|
var StoreContext = createContext(null);
|
|
5
4
|
var StoreProvider = StoreContext.Provider;
|
|
6
5
|
function useStore() {
|
|
7
6
|
var store = useContext(StoreContext);
|
|
8
7
|
if (!store) {
|
|
9
|
-
|
|
8
|
+
throw new Error('useStore must be used within a StoreProvider');
|
|
10
9
|
}
|
|
11
10
|
return store;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
function
|
|
15
|
-
var silenceUnhandleRejection = _ref.silenceUnhandleRejection;
|
|
13
|
+
function useGet(atom) {
|
|
16
14
|
var store = useStore();
|
|
17
|
-
|
|
18
|
-
var
|
|
19
|
-
store.
|
|
20
|
-
|
|
15
|
+
var onChange = useRef(function (fn) {
|
|
16
|
+
var controller = new AbortController();
|
|
17
|
+
store.watch(function (get) {
|
|
18
|
+
get(atom);
|
|
19
|
+
fn();
|
|
20
|
+
}, {
|
|
21
|
+
signal: controller.signal
|
|
21
22
|
});
|
|
22
23
|
return function () {
|
|
23
|
-
|
|
24
|
+
controller.abort();
|
|
24
25
|
};
|
|
25
|
-
}, function () {
|
|
26
|
-
var val = store.get(atom);
|
|
27
|
-
if (val instanceof Promise && silenceUnhandleRejection) {
|
|
28
|
-
val["catch"](function () {
|
|
29
|
-
return void 0;
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
return val;
|
|
33
26
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return useGetInternal(atom, {
|
|
37
|
-
silenceUnhandleRejection: false
|
|
27
|
+
return useSyncExternalStore(onChange.current, function () {
|
|
28
|
+
return store.get(atom);
|
|
38
29
|
});
|
|
39
30
|
}
|
|
40
31
|
|
|
@@ -50,95 +41,54 @@ function useSet(signal) {
|
|
|
50
41
|
}, [store, signal]);
|
|
51
42
|
}
|
|
52
43
|
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return n;
|
|
57
|
-
}
|
|
58
|
-
function _arrayWithHoles(r) {
|
|
59
|
-
if (Array.isArray(r)) return r;
|
|
60
|
-
}
|
|
61
|
-
function _iterableToArrayLimit(r, l) {
|
|
62
|
-
var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
|
|
63
|
-
if (null != t) {
|
|
64
|
-
var e,
|
|
65
|
-
n,
|
|
66
|
-
i,
|
|
67
|
-
u,
|
|
68
|
-
a = [],
|
|
69
|
-
f = !0,
|
|
70
|
-
o = !1;
|
|
71
|
-
try {
|
|
72
|
-
if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
|
|
73
|
-
} catch (r) {
|
|
74
|
-
o = !0, n = r;
|
|
75
|
-
} finally {
|
|
76
|
-
try {
|
|
77
|
-
if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
|
|
78
|
-
} finally {
|
|
79
|
-
if (o) throw n;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return a;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
function _nonIterableRest() {
|
|
86
|
-
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
87
|
-
}
|
|
88
|
-
function _slicedToArray(r, e) {
|
|
89
|
-
return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
|
|
90
|
-
}
|
|
91
|
-
function _unsupportedIterableToArray(r, a) {
|
|
92
|
-
if (r) {
|
|
93
|
-
if ("string" == typeof r) return _arrayLikeToArray(r, a);
|
|
94
|
-
var t = {}.toString.call(r).slice(8, -1);
|
|
95
|
-
return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function useLoadableInternal(atom, keepLastResolved) {
|
|
100
|
-
var promise = useGetInternal(atom, {
|
|
101
|
-
silenceUnhandleRejection: true
|
|
44
|
+
function useLoadableInternal(promise$, keepLastResolved) {
|
|
45
|
+
var promiseResult = useRef({
|
|
46
|
+
state: 'loading'
|
|
102
47
|
});
|
|
103
|
-
var
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
_useState2 = _slicedToArray(_useState, 2),
|
|
107
|
-
promiseResult = _useState2[0],
|
|
108
|
-
setPromiseResult = _useState2[1];
|
|
109
|
-
useEffect(function () {
|
|
110
|
-
if (!(promise instanceof Promise)) {
|
|
111
|
-
setPromiseResult({
|
|
112
|
-
state: 'hasData',
|
|
113
|
-
data: promise
|
|
114
|
-
});
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
var ctrl = new AbortController();
|
|
118
|
-
var signal = ctrl.signal;
|
|
119
|
-
if (!keepLastResolved) {
|
|
120
|
-
setPromiseResult({
|
|
121
|
-
state: 'loading'
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
promise.then(function (ret) {
|
|
125
|
-
if (signal.aborted) return;
|
|
126
|
-
setPromiseResult({
|
|
127
|
-
state: 'hasData',
|
|
128
|
-
data: ret
|
|
129
|
-
});
|
|
130
|
-
}, function (error) {
|
|
48
|
+
var store = useStore();
|
|
49
|
+
var subStore = useCallback(function (fn) {
|
|
50
|
+
function updateResult(result, signal) {
|
|
131
51
|
if (signal.aborted) return;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
52
|
+
promiseResult.current = result;
|
|
53
|
+
fn();
|
|
54
|
+
}
|
|
55
|
+
var controller = new AbortController();
|
|
56
|
+
store.watch(function (get, _ref) {
|
|
57
|
+
var signal = _ref.signal;
|
|
58
|
+
var promise = get(promise$);
|
|
59
|
+
if (!(promise instanceof Promise)) {
|
|
60
|
+
updateResult({
|
|
61
|
+
state: 'hasData',
|
|
62
|
+
data: promise
|
|
63
|
+
}, signal);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!keepLastResolved) {
|
|
67
|
+
updateResult({
|
|
68
|
+
state: 'loading'
|
|
69
|
+
}, signal);
|
|
70
|
+
}
|
|
71
|
+
promise.then(function (ret) {
|
|
72
|
+
updateResult({
|
|
73
|
+
state: 'hasData',
|
|
74
|
+
data: ret
|
|
75
|
+
}, signal);
|
|
76
|
+
}, function (error) {
|
|
77
|
+
updateResult({
|
|
78
|
+
state: 'hasError',
|
|
79
|
+
error: error
|
|
80
|
+
}, signal);
|
|
135
81
|
});
|
|
82
|
+
}, {
|
|
83
|
+
signal: controller.signal
|
|
136
84
|
});
|
|
137
85
|
return function () {
|
|
138
|
-
|
|
86
|
+
controller.abort();
|
|
139
87
|
};
|
|
140
|
-
}, [promise]);
|
|
141
|
-
return
|
|
88
|
+
}, [store, promise$]);
|
|
89
|
+
return useSyncExternalStore(subStore, function () {
|
|
90
|
+
return promiseResult.current;
|
|
91
|
+
});
|
|
142
92
|
}
|
|
143
93
|
function useLoadable(atom) {
|
|
144
94
|
return useLoadableInternal(atom, false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ccstate-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "CCState React Hooks",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"react": ">=17.0.0"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"ccstate": "^
|
|
28
|
+
"ccstate": "^5.0.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependenciesMeta": {
|
|
31
31
|
"@types/react": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"shx": "^0.3.4",
|
|
56
56
|
"signal-timers": "^1.0.4",
|
|
57
57
|
"vitest": "^2.1.8",
|
|
58
|
-
"ccstate": "^
|
|
58
|
+
"ccstate": "^5.0.0"
|
|
59
59
|
},
|
|
60
60
|
"scripts": {
|
|
61
61
|
"build": "rollup -c",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { render, cleanup, screen } from '@testing-library/react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import { computed, createStore, command, state, createDebugStore
|
|
4
|
+
import { computed, createStore, command, state, createDebugStore } from 'ccstate';
|
|
5
5
|
import { StoreProvider, useGet, useSet } from '..';
|
|
6
6
|
import { StrictMode, useState } from 'react';
|
|
7
7
|
import '@testing-library/jest-dom/vitest';
|
|
@@ -198,21 +198,21 @@ describe('react', () => {
|
|
|
198
198
|
expect(await screen.findByText('1')).toBeInTheDocument();
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
-
it('
|
|
201
|
+
it('throw error if no store provide', () => {
|
|
202
202
|
const count$ = state(0);
|
|
203
|
-
getDefaultStore().set(count$, 10);
|
|
204
203
|
|
|
205
204
|
function App() {
|
|
206
205
|
const count = useGet(count$);
|
|
207
206
|
return <div>{count}</div>;
|
|
208
207
|
}
|
|
209
208
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
209
|
+
expect(() => {
|
|
210
|
+
render(
|
|
211
|
+
<StrictMode>
|
|
212
|
+
<App />
|
|
213
|
+
</StrictMode>,
|
|
214
|
+
);
|
|
215
|
+
}).toThrowError('useStore must be used within a StoreProvider');
|
|
216
216
|
});
|
|
217
217
|
|
|
218
218
|
it('will unmount when component cleanup', async () => {
|
|
@@ -221,7 +221,7 @@ describe('react', () => {
|
|
|
221
221
|
|
|
222
222
|
function App() {
|
|
223
223
|
const ret = useGet(base$);
|
|
224
|
-
return <div>{ret}</div>;
|
|
224
|
+
return <div>ret:{ret}</div>;
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
function Container() {
|
|
@@ -252,12 +252,16 @@ describe('react', () => {
|
|
|
252
252
|
);
|
|
253
253
|
|
|
254
254
|
const user = userEvent.setup();
|
|
255
|
-
|
|
255
|
+
|
|
256
|
+
expect(screen.getByText('ret:0')).toBeInTheDocument();
|
|
256
257
|
const button = screen.getByText('hide');
|
|
258
|
+
|
|
259
|
+
expect(store.getReadDependents(base$)).toHaveLength(2);
|
|
260
|
+
|
|
257
261
|
expect(button).toBeInTheDocument();
|
|
258
262
|
await user.click(button);
|
|
259
263
|
expect(await screen.findByText('unmounted')).toBeInTheDocument();
|
|
260
|
-
expect(store.
|
|
264
|
+
expect(store.getReadDependents(base$)).toHaveLength(1);
|
|
261
265
|
});
|
|
262
266
|
});
|
|
263
267
|
|
|
@@ -287,7 +291,12 @@ it('useSet should be stable', () => {
|
|
|
287
291
|
return <div>Render</div>;
|
|
288
292
|
}
|
|
289
293
|
|
|
290
|
-
|
|
294
|
+
const store = createStore();
|
|
295
|
+
render(
|
|
296
|
+
<StoreProvider value={store}>
|
|
297
|
+
<Container />
|
|
298
|
+
</StoreProvider>,
|
|
299
|
+
);
|
|
291
300
|
|
|
292
301
|
expect(trace).toHaveBeenCalledTimes(2);
|
|
293
302
|
expect(trace.mock.calls[0][0]).toBe(trace.mock.calls[1][0]);
|
|
@@ -4,8 +4,8 @@ import '@testing-library/jest-dom/vitest';
|
|
|
4
4
|
import { screen, cleanup, render } from '@testing-library/react';
|
|
5
5
|
import { StrictMode } from 'react';
|
|
6
6
|
import userEvent from '@testing-library/user-event';
|
|
7
|
-
import { useCCState, useCommand, useComputed
|
|
8
|
-
import {
|
|
7
|
+
import { useCCState, useCommand, useComputed } from '../experimental';
|
|
8
|
+
import { createStore } from 'ccstate';
|
|
9
9
|
|
|
10
10
|
afterEach(() => {
|
|
11
11
|
cleanup();
|
|
@@ -62,9 +62,12 @@ it('use atom in React component', async () => {
|
|
|
62
62
|
);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
const store = createStore();
|
|
65
66
|
render(
|
|
66
67
|
<StrictMode>
|
|
67
|
-
<
|
|
68
|
+
<StoreProvider value={store}>
|
|
69
|
+
<Counter />
|
|
70
|
+
</StoreProvider>
|
|
68
71
|
</StrictMode>,
|
|
69
72
|
);
|
|
70
73
|
expect(screen.getByText('count: 0')).toBeInTheDocument();
|
|
@@ -79,61 +82,3 @@ it('use atom in React component', async () => {
|
|
|
79
82
|
expect(screen.getByText('count: 4')).toBeInTheDocument();
|
|
80
83
|
expect(screen.getByText('double: 8')).toBeInTheDocument();
|
|
81
84
|
});
|
|
82
|
-
|
|
83
|
-
it('use sub in React component', async () => {
|
|
84
|
-
function Counter() {
|
|
85
|
-
const count$ = useCCState(0, {
|
|
86
|
-
debugLabel: 'count$',
|
|
87
|
-
});
|
|
88
|
-
const double$ = useCCState(0, {
|
|
89
|
-
debugLabel: 'double$',
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const updateDouble$ = useCommand(
|
|
93
|
-
({ get, set }) => {
|
|
94
|
-
const double = get(count$) * 2;
|
|
95
|
-
set(double$, double);
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
debugLabel: 'updateDouble$',
|
|
99
|
-
},
|
|
100
|
-
);
|
|
101
|
-
useSub(count$, updateDouble$);
|
|
102
|
-
|
|
103
|
-
const count = useGet(count$);
|
|
104
|
-
const double = useGet(double$);
|
|
105
|
-
const setCount = useSet(count$);
|
|
106
|
-
|
|
107
|
-
return (
|
|
108
|
-
<>
|
|
109
|
-
<div>count: {String(count)}</div>
|
|
110
|
-
<div>double: {String(double)}</div>
|
|
111
|
-
<button
|
|
112
|
-
onClick={() => {
|
|
113
|
-
setCount((prev) => prev + 1);
|
|
114
|
-
}}
|
|
115
|
-
>
|
|
116
|
-
Increment
|
|
117
|
-
</button>
|
|
118
|
-
</>
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const store = createDebugStore();
|
|
123
|
-
render(
|
|
124
|
-
<StrictMode>
|
|
125
|
-
<StoreProvider value={store}>
|
|
126
|
-
<Counter />
|
|
127
|
-
</StoreProvider>
|
|
128
|
-
</StrictMode>,
|
|
129
|
-
);
|
|
130
|
-
expect(screen.getByText('count: 0')).toBeInTheDocument();
|
|
131
|
-
|
|
132
|
-
const button = screen.getByText('Increment');
|
|
133
|
-
await user.click(button);
|
|
134
|
-
expect(screen.getByText('count: 1')).toBeInTheDocument();
|
|
135
|
-
expect(await screen.findByText('double: 2')).toBeInTheDocument();
|
|
136
|
-
|
|
137
|
-
cleanup();
|
|
138
|
-
expect(store.getSubscribeGraph()).toEqual([]);
|
|
139
|
-
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import '@testing-library/jest-dom/vitest';
|
|
4
4
|
import { render, cleanup, screen } from '@testing-library/react';
|
|
5
5
|
import userEvent from '@testing-library/user-event';
|
|
6
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
6
|
+
import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest';
|
|
7
7
|
import { computed, createStore, state } from 'ccstate';
|
|
8
8
|
import type { Computed, State } from 'ccstate';
|
|
9
9
|
import { StrictMode, useEffect } from 'react';
|
|
@@ -539,7 +539,12 @@ it('useLoadable accept sync computed', async () => {
|
|
|
539
539
|
return <div>{base.state}</div>;
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
-
|
|
542
|
+
const store = createStore();
|
|
543
|
+
render(
|
|
544
|
+
<StoreProvider value={store}>
|
|
545
|
+
<App />
|
|
546
|
+
</StoreProvider>,
|
|
547
|
+
);
|
|
543
548
|
|
|
544
549
|
expect(await screen.findByText('hasData')).toBeInTheDocument();
|
|
545
550
|
});
|
|
@@ -583,9 +588,11 @@ describe('works with AbortError', () => {
|
|
|
583
588
|
}
|
|
584
589
|
|
|
585
590
|
render(
|
|
586
|
-
<
|
|
587
|
-
<
|
|
588
|
-
|
|
591
|
+
<StrictMode>
|
|
592
|
+
<StoreProvider value={store}>
|
|
593
|
+
<App />
|
|
594
|
+
</StoreProvider>
|
|
595
|
+
</StrictMode>,
|
|
589
596
|
);
|
|
590
597
|
|
|
591
598
|
expect(screen.getByText('Test')).toBeInTheDocument();
|
|
@@ -594,3 +601,44 @@ describe('works with AbortError', () => {
|
|
|
594
601
|
store.set(reload$, (x) => x + 1);
|
|
595
602
|
});
|
|
596
603
|
});
|
|
604
|
+
|
|
605
|
+
test('useLoadable should catch errors', () => {
|
|
606
|
+
const reload$ = state(0);
|
|
607
|
+
|
|
608
|
+
const traceCatch = vi.fn();
|
|
609
|
+
const promise$ = computed((get) => {
|
|
610
|
+
get(reload$);
|
|
611
|
+
|
|
612
|
+
const p = Promise.resolve();
|
|
613
|
+
const originalThen = p.then.bind(p);
|
|
614
|
+
vi.spyOn(p, 'then').mockImplementation((...args) => {
|
|
615
|
+
traceCatch();
|
|
616
|
+
return originalThen(...args);
|
|
617
|
+
});
|
|
618
|
+
return p;
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const store = createStore();
|
|
622
|
+
|
|
623
|
+
function App() {
|
|
624
|
+
useLoadable(promise$);
|
|
625
|
+
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
render(
|
|
630
|
+
<StrictMode>
|
|
631
|
+
<StoreProvider value={store}>
|
|
632
|
+
<App />
|
|
633
|
+
</StoreProvider>
|
|
634
|
+
</StrictMode>,
|
|
635
|
+
);
|
|
636
|
+
expect(traceCatch).toHaveBeenCalledTimes(2); // strict mode renders twice
|
|
637
|
+
|
|
638
|
+
store.set(reload$, (x) => x + 1);
|
|
639
|
+
expect(traceCatch).toHaveBeenCalledTimes(3);
|
|
640
|
+
|
|
641
|
+
store.set(reload$, (x) => x + 1);
|
|
642
|
+
store.set(reload$, (x) => x + 1);
|
|
643
|
+
expect(traceCatch).toHaveBeenCalledTimes(5);
|
|
644
|
+
});
|
|
@@ -109,7 +109,12 @@ it('useResolved accept sync computed', async () => {
|
|
|
109
109
|
return <div>{base}</div>;
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
const store = createStore();
|
|
113
|
+
render(
|
|
114
|
+
<StoreProvider value={store}>
|
|
115
|
+
<App />
|
|
116
|
+
</StoreProvider>,
|
|
117
|
+
);
|
|
113
118
|
|
|
114
119
|
expect(await screen.findByText('0')).toBeInTheDocument();
|
|
115
120
|
});
|
package/src/experimental.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { useCCState, useComputed, useCommand
|
|
1
|
+
export { useCCState, useComputed, useCommand } from './useInlineAtom';
|
package/src/provider.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
|
-
import { getDefaultStore } from 'ccstate';
|
|
3
2
|
import type { Store } from 'ccstate';
|
|
4
3
|
|
|
5
4
|
const StoreContext = createContext<Store | null>(null);
|
|
@@ -10,7 +9,7 @@ export function useStore(): Store {
|
|
|
10
9
|
const store = useContext(StoreContext);
|
|
11
10
|
|
|
12
11
|
if (!store) {
|
|
13
|
-
|
|
12
|
+
throw new Error('useStore must be used within a StoreProvider');
|
|
14
13
|
}
|
|
15
14
|
|
|
16
15
|
return store;
|
package/src/useGet.ts
CHANGED
|
@@ -1,31 +1,25 @@
|
|
|
1
|
-
import { useSyncExternalStore } from 'react';
|
|
1
|
+
import { useRef, useSyncExternalStore } from 'react';
|
|
2
2
|
import { useStore } from './provider';
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
import type { Computed, State } from 'ccstate';
|
|
5
5
|
|
|
6
|
-
export function
|
|
7
|
-
atom: State<T> | Computed<T>,
|
|
8
|
-
{ silenceUnhandleRejection }: { silenceUnhandleRejection: boolean },
|
|
9
|
-
) {
|
|
6
|
+
export function useGet<T>(atom: State<T> | Computed<T>) {
|
|
10
7
|
const store = useStore();
|
|
11
|
-
|
|
12
|
-
(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
);
|
|
27
|
-
}
|
|
8
|
+
const onChange = useRef((fn: () => void) => {
|
|
9
|
+
const controller = new AbortController();
|
|
10
|
+
store.watch(
|
|
11
|
+
(get) => {
|
|
12
|
+
get(atom);
|
|
13
|
+
fn();
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
signal: controller.signal,
|
|
17
|
+
},
|
|
18
|
+
);
|
|
19
|
+
return () => {
|
|
20
|
+
controller.abort();
|
|
21
|
+
};
|
|
22
|
+
});
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
return useGetInternal(atom, { silenceUnhandleRejection: false });
|
|
24
|
+
return useSyncExternalStore(onChange.current, () => store.get(atom));
|
|
31
25
|
}
|
package/src/useInlineAtom.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { command, computed, state, type Command, type Computed, type State
|
|
2
|
-
import {
|
|
3
|
-
import { useStore } from './provider';
|
|
1
|
+
import { command, computed, state, type Command, type Computed, type State } from 'ccstate';
|
|
2
|
+
import { useRef } from 'react';
|
|
4
3
|
|
|
5
4
|
function useRefFactory<T>(factory: () => T): T {
|
|
6
5
|
const ref = useRef<T | null>(null);
|
|
@@ -30,11 +29,3 @@ export function useCommand<T, Args extends unknown[]>(...args: Parameters<typeof
|
|
|
30
29
|
return command(...args);
|
|
31
30
|
});
|
|
32
31
|
}
|
|
33
|
-
|
|
34
|
-
export function useSub(...args: Parameters<Subscribe>) {
|
|
35
|
-
const store = useStore();
|
|
36
|
-
|
|
37
|
-
useEffect(() => {
|
|
38
|
-
return store.sub(...args);
|
|
39
|
-
}, []);
|
|
40
|
-
}
|
package/src/useLoadable.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
1
|
+
import { useCallback, useRef, useSyncExternalStore } from 'react';
|
|
2
|
+
import { type Computed, type State } from 'ccstate';
|
|
3
|
+
import { useStore } from './provider';
|
|
4
4
|
|
|
5
5
|
type Loadable<T> =
|
|
6
6
|
| {
|
|
@@ -8,79 +8,99 @@ type Loadable<T> =
|
|
|
8
8
|
}
|
|
9
9
|
| {
|
|
10
10
|
state: 'hasData';
|
|
11
|
-
data: T
|
|
11
|
+
data: Awaited<T>;
|
|
12
12
|
}
|
|
13
13
|
| {
|
|
14
14
|
state: 'hasError';
|
|
15
15
|
error: unknown;
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
-
function useLoadableInternal<T
|
|
19
|
-
|
|
18
|
+
function useLoadableInternal<T, U extends Promise<Awaited<T>> | Awaited<T>>(
|
|
19
|
+
promise$: State<U> | Computed<U>,
|
|
20
20
|
keepLastResolved: boolean,
|
|
21
21
|
): Loadable<T> {
|
|
22
|
-
const
|
|
23
|
-
silenceUnhandleRejection: true,
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const [promiseResult, setPromiseResult] = useState<Loadable<T>>({
|
|
22
|
+
const promiseResult = useRef<Loadable<T>>({
|
|
27
23
|
state: 'loading',
|
|
28
24
|
});
|
|
29
25
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const ctrl = new AbortController();
|
|
41
|
-
const signal = ctrl.signal;
|
|
26
|
+
const store = useStore();
|
|
27
|
+
const subStore = useCallback(
|
|
28
|
+
(fn: () => void) => {
|
|
29
|
+
function updateResult(result: Loadable<T>, signal: AbortSignal) {
|
|
30
|
+
if (signal.aborted) return;
|
|
31
|
+
promiseResult.current = result;
|
|
32
|
+
fn();
|
|
33
|
+
}
|
|
42
34
|
|
|
43
|
-
|
|
44
|
-
setPromiseResult({
|
|
45
|
-
state: 'loading',
|
|
46
|
-
});
|
|
47
|
-
}
|
|
35
|
+
const controller = new AbortController();
|
|
48
36
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
37
|
+
store.watch(
|
|
38
|
+
(get, { signal }) => {
|
|
39
|
+
const promise: Promise<Awaited<T>> | Awaited<T> = get(promise$);
|
|
40
|
+
if (!(promise instanceof Promise)) {
|
|
41
|
+
updateResult(
|
|
42
|
+
{
|
|
43
|
+
state: 'hasData',
|
|
44
|
+
data: promise,
|
|
45
|
+
},
|
|
46
|
+
signal,
|
|
47
|
+
);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
if (!keepLastResolved) {
|
|
52
|
+
updateResult(
|
|
53
|
+
{
|
|
54
|
+
state: 'loading',
|
|
55
|
+
},
|
|
56
|
+
signal,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
promise.then(
|
|
61
|
+
(ret) => {
|
|
62
|
+
updateResult(
|
|
63
|
+
{
|
|
64
|
+
state: 'hasData',
|
|
65
|
+
data: ret,
|
|
66
|
+
},
|
|
67
|
+
signal,
|
|
68
|
+
);
|
|
69
|
+
},
|
|
70
|
+
(error: unknown) => {
|
|
71
|
+
updateResult(
|
|
72
|
+
{
|
|
73
|
+
state: 'hasError',
|
|
74
|
+
error,
|
|
75
|
+
},
|
|
76
|
+
signal,
|
|
77
|
+
);
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
signal: controller.signal,
|
|
83
|
+
},
|
|
84
|
+
);
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
86
|
+
return () => {
|
|
87
|
+
controller.abort();
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
[store, promise$],
|
|
91
|
+
);
|
|
72
92
|
|
|
73
|
-
return promiseResult;
|
|
93
|
+
return useSyncExternalStore(subStore, () => promiseResult.current);
|
|
74
94
|
}
|
|
75
95
|
|
|
76
96
|
export function useLoadable<T>(
|
|
77
97
|
atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>,
|
|
78
|
-
): Loadable<
|
|
98
|
+
): Loadable<T> {
|
|
79
99
|
return useLoadableInternal(atom, false);
|
|
80
100
|
}
|
|
81
101
|
|
|
82
102
|
export function useLastLoadable<T>(
|
|
83
103
|
atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>,
|
|
84
|
-
): Loadable<
|
|
104
|
+
): Loadable<T> {
|
|
85
105
|
return useLoadableInternal(atom, true);
|
|
86
106
|
}
|
package/src/useResolved.ts
CHANGED
|
@@ -4,13 +4,13 @@ import type { Computed, State } from 'ccstate';
|
|
|
4
4
|
export function useResolved<T>(
|
|
5
5
|
atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>,
|
|
6
6
|
): Awaited<T> | undefined {
|
|
7
|
-
const loadable = useLoadable(atom);
|
|
7
|
+
const loadable = useLoadable<T>(atom);
|
|
8
8
|
return loadable.state === 'hasData' ? loadable.data : undefined;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function useLastResolved<T>(
|
|
12
12
|
atom: State<Promise<Awaited<T>> | Awaited<T>> | Computed<Promise<Awaited<T>> | Awaited<T>>,
|
|
13
13
|
): Awaited<T> | undefined {
|
|
14
|
-
const loadable = useLastLoadable(atom);
|
|
14
|
+
const loadable = useLastLoadable<T>(atom);
|
|
15
15
|
return loadable.state === 'hasData' ? loadable.data : undefined;
|
|
16
16
|
}
|