@kuindji/reactive 1.0.23 → 1.1.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 +34 -11
- package/dist/action.d.ts +12 -10
- package/dist/action.js +23 -16
- package/dist/actionBus.d.ts +8 -4
- package/dist/actionBus.js +37 -8
- package/dist/actionMap.d.ts +21 -19
- package/dist/actionMap.js +12 -9
- package/dist/event.d.ts +3 -2
- package/dist/event.js +160 -109
- package/dist/eventBus.d.ts +5 -3
- package/dist/eventBus.js +158 -104
- package/dist/index.d.ts +7 -7
- package/dist/index.js +7 -23
- package/dist/lib/actionMapInternal.d.ts +8 -0
- package/dist/lib/actionMapInternal.js +8 -0
- package/dist/lib/asyncCall.js +1 -4
- package/dist/lib/isPromiseLike.d.ts +1 -0
- package/dist/lib/isPromiseLike.js +5 -0
- package/dist/lib/listenerSorter.js +1 -4
- package/dist/lib/normalizeEventOptions.d.ts +13 -0
- package/dist/lib/normalizeEventOptions.js +21 -0
- package/dist/lib/tagsIntersect.js +1 -4
- package/dist/lib/types.js +4 -7
- package/dist/react/ErrorBoundary.d.ts +1 -1
- package/dist/react/ErrorBoundary.js +10 -13
- package/dist/react/listenerOptionsEqual.d.ts +27 -0
- package/dist/react/listenerOptionsEqual.js +121 -0
- package/dist/react/useAction.d.ts +3 -3
- package/dist/react/useAction.js +25 -25
- package/dist/react/useActionBus.d.ts +4 -4
- package/dist/react/useActionBus.js +41 -14
- package/dist/react/useActionMap.d.ts +4 -4
- package/dist/react/useActionMap.js +46 -16
- package/dist/react/useEvent.d.ts +2 -2
- package/dist/react/useEvent.js +30 -17
- package/dist/react/useEventBus.d.ts +2 -2
- package/dist/react/useEventBus.js +27 -26
- package/dist/react/useListenToAction.d.ts +1 -1
- package/dist/react/useListenToAction.js +24 -48
- package/dist/react/useListenToActionBus.d.ts +3 -3
- package/dist/react/useListenToActionBus.js +22 -19
- package/dist/react/useListenToEvent.d.ts +2 -2
- package/dist/react/useListenToEvent.js +13 -14
- package/dist/react/useListenToEventBus.d.ts +3 -3
- package/dist/react/useListenToEventBus.js +14 -15
- package/dist/react/useListenToStoreChanges.d.ts +3 -3
- package/dist/react/useListenToStoreChanges.js +12 -13
- package/dist/react/useReconciledListener.d.ts +33 -0
- package/dist/react/useReconciledListener.js +44 -0
- package/dist/react/useStore.d.ts +2 -2
- package/dist/react/useStore.js +72 -23
- package/dist/react/useStoreState.d.ts +2 -2
- package/dist/react/useStoreState.js +16 -26
- package/dist/react.d.ts +13 -13
- package/dist/react.js +13 -29
- package/dist/store.d.ts +9 -8
- package/dist/store.js +116 -53
- package/package.json +13 -3
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
exports.ErrorBoundary = ErrorBoundary;
|
|
5
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
-
const react_1 = require("react");
|
|
7
|
-
exports.ErrorBoundaryContext = (0, react_1.createContext)(null);
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useContext, useRef } from "react";
|
|
3
|
+
export const ErrorBoundaryContext = createContext(null);
|
|
8
4
|
function ErrorBoundary({ children, listener }) {
|
|
9
|
-
const boundaryErrorListener =
|
|
10
|
-
const thisRef =
|
|
11
|
-
const outerRef =
|
|
5
|
+
const boundaryErrorListener = useContext(ErrorBoundaryContext);
|
|
6
|
+
const thisRef = useRef(listener);
|
|
7
|
+
const outerRef = useRef(boundaryErrorListener);
|
|
12
8
|
// Keep refs in sync with props
|
|
13
9
|
thisRef.current = listener;
|
|
14
10
|
outerRef.current = boundaryErrorListener;
|
|
15
|
-
const thisErrorListener =
|
|
11
|
+
const thisErrorListener = useCallback((errorResponse) => {
|
|
16
12
|
if (thisRef.current) {
|
|
17
13
|
thisRef.current(errorResponse);
|
|
18
14
|
}
|
|
@@ -23,6 +19,7 @@ function ErrorBoundary({ children, listener }) {
|
|
|
23
19
|
throw errorResponse.error;
|
|
24
20
|
}
|
|
25
21
|
}, []);
|
|
26
|
-
return ((
|
|
22
|
+
return (_jsx(ErrorBoundaryContext.Provider, { value: thisErrorListener, children: children }));
|
|
27
23
|
}
|
|
28
|
-
|
|
24
|
+
export default ErrorBoundary;
|
|
25
|
+
export { ErrorBoundary };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { EventOptions, ListenerOptions } from "../event.js";
|
|
2
|
+
import type { BaseEventMap, EventBusOptions } from "../eventBus.js";
|
|
3
|
+
import type { BaseHandler } from "../lib/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Order-insensitive set comparison for listener tags.
|
|
6
|
+
* `undefined`, `[]` and missing all compare equal. Order and duplicates do
|
|
7
|
+
* not matter because the core only ever uses tags via membership and
|
|
8
|
+
* intersection checks.
|
|
9
|
+
*/
|
|
10
|
+
export declare function areTagsEqual(a?: string[], b?: string[]): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Domain-specific comparator for {@link ListenerOptions}. Avoids generic deep
|
|
13
|
+
* equality: primitives compare after default semantics, `context`/`extraData`
|
|
14
|
+
* compare by reference, and `tags` use order-insensitive set comparison.
|
|
15
|
+
*/
|
|
16
|
+
export declare function areListenerOptionsEqual(a?: ListenerOptions | null, b?: ListenerOptions | null): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Domain-specific comparator for {@link EventOptions}. Primitives compare after
|
|
19
|
+
* default semantics; `filter`/`filterContext` compare by reference.
|
|
20
|
+
*/
|
|
21
|
+
export declare function areEventOptionsEqual(a?: EventOptions<BaseHandler> | null, b?: EventOptions<BaseHandler> | null): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Compares the per-event `eventOptions` maps of two {@link EventBusOptions}.
|
|
24
|
+
* Equal when every event name present in either map has semantically equal
|
|
25
|
+
* {@link EventOptions} (missing entries compare as default options).
|
|
26
|
+
*/
|
|
27
|
+
export declare function areEventBusOptionsEqual(a?: EventBusOptions<BaseEventMap> | null, b?: EventBusOptions<BaseEventMap> | null): boolean;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
function normalizeAsync(value) {
|
|
2
|
+
if (value === undefined || value === null) {
|
|
3
|
+
return null;
|
|
4
|
+
}
|
|
5
|
+
if (value === true) {
|
|
6
|
+
return 1;
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Order-insensitive set comparison for listener tags.
|
|
12
|
+
* `undefined`, `[]` and missing all compare equal. Order and duplicates do
|
|
13
|
+
* not matter because the core only ever uses tags via membership and
|
|
14
|
+
* intersection checks.
|
|
15
|
+
*/
|
|
16
|
+
export function areTagsEqual(a, b) {
|
|
17
|
+
const aa = a !== null && a !== void 0 ? a : [];
|
|
18
|
+
const bb = b !== null && b !== void 0 ? b : [];
|
|
19
|
+
if (aa.length === 0 && bb.length === 0) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
const sa = new Set(aa);
|
|
23
|
+
const sb = new Set(bb);
|
|
24
|
+
if (sa.size !== sb.size) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
for (const tag of sa) {
|
|
28
|
+
if (!sb.has(tag)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Domain-specific comparator for {@link ListenerOptions}. Avoids generic deep
|
|
36
|
+
* equality: primitives compare after default semantics, `context`/`extraData`
|
|
37
|
+
* compare by reference, and `tags` use order-insensitive set comparison.
|
|
38
|
+
*/
|
|
39
|
+
export function areListenerOptionsEqual(a, b) {
|
|
40
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
41
|
+
const aa = a !== null && a !== void 0 ? a : {};
|
|
42
|
+
const bb = b !== null && b !== void 0 ? b : {};
|
|
43
|
+
if (((_a = aa.limit) !== null && _a !== void 0 ? _a : 0) !== ((_b = bb.limit) !== null && _b !== void 0 ? _b : 0)) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (((_c = aa.start) !== null && _c !== void 0 ? _c : 1) !== ((_d = bb.start) !== null && _d !== void 0 ? _d : 1)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (((_e = aa.first) !== null && _e !== void 0 ? _e : false) !== ((_f = bb.first) !== null && _f !== void 0 ? _f : false)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (((_g = aa.alwaysFirst) !== null && _g !== void 0 ? _g : false) !== ((_h = bb.alwaysFirst) !== null && _h !== void 0 ? _h : false)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (((_j = aa.alwaysLast) !== null && _j !== void 0 ? _j : false) !== ((_k = bb.alwaysLast) !== null && _k !== void 0 ? _k : false)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (normalizeAsync(aa.async) !== normalizeAsync(bb.async)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (((_l = aa.context) !== null && _l !== void 0 ? _l : null) !== ((_m = bb.context) !== null && _m !== void 0 ? _m : null)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
// reference equality only; do not deep compare arbitrary values
|
|
65
|
+
if (aa.extraData !== bb.extraData) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (!areTagsEqual(aa.tags, bb.tags)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Domain-specific comparator for {@link EventOptions}. Primitives compare after
|
|
75
|
+
* default semantics; `filter`/`filterContext` compare by reference.
|
|
76
|
+
*/
|
|
77
|
+
export function areEventOptionsEqual(a, b) {
|
|
78
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
79
|
+
const aa = a !== null && a !== void 0 ? a : {};
|
|
80
|
+
const bb = b !== null && b !== void 0 ? b : {};
|
|
81
|
+
if (normalizeAsync(aa.async) !== normalizeAsync(bb.async)) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (((_a = aa.limit) !== null && _a !== void 0 ? _a : null) !== ((_b = bb.limit) !== null && _b !== void 0 ? _b : null)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (((_c = aa.autoTrigger) !== null && _c !== void 0 ? _c : null) !== ((_d = bb.autoTrigger) !== null && _d !== void 0 ? _d : null)) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
if (((_e = aa.maxListeners) !== null && _e !== void 0 ? _e : 0) !== ((_f = bb.maxListeners) !== null && _f !== void 0 ? _f : 0)) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// reference equality
|
|
94
|
+
if (((_g = aa.filter) !== null && _g !== void 0 ? _g : null) !== ((_h = bb.filter) !== null && _h !== void 0 ? _h : null)) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (((_j = aa.filterContext) !== null && _j !== void 0 ? _j : null) !== ((_k = bb.filterContext) !== null && _k !== void 0 ? _k : null)) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Compares the per-event `eventOptions` maps of two {@link EventBusOptions}.
|
|
104
|
+
* Equal when every event name present in either map has semantically equal
|
|
105
|
+
* {@link EventOptions} (missing entries compare as default options).
|
|
106
|
+
*/
|
|
107
|
+
export function areEventBusOptionsEqual(a, b) {
|
|
108
|
+
var _a, _b;
|
|
109
|
+
const aMap = (_a = a === null || a === void 0 ? void 0 : a.eventOptions) !== null && _a !== void 0 ? _a : {};
|
|
110
|
+
const bMap = (_b = b === null || b === void 0 ? void 0 : b.eventOptions) !== null && _b !== void 0 ? _b : {};
|
|
111
|
+
const names = new Set([
|
|
112
|
+
...Object.keys(aMap),
|
|
113
|
+
...Object.keys(bMap),
|
|
114
|
+
]);
|
|
115
|
+
for (const name of names) {
|
|
116
|
+
if (!areEventOptionsEqual(aMap[name], bMap[name])) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createAction } from "../action";
|
|
2
|
-
import type { ActionResponse, BeforeActionSignature, ListenerSignature } from "../action";
|
|
3
|
-
import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types";
|
|
1
|
+
import { createAction } from "../action.js";
|
|
2
|
+
import type { ActionResponse, BeforeActionSignature, ListenerSignature } from "../action.js";
|
|
3
|
+
import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
|
|
4
4
|
export type { ActionResponse, BaseHandler, ErrorListenerSignature, ErrorResponse, ListenerSignature, };
|
|
5
5
|
export declare function useAction<ActionSignature extends BaseHandler, Listener extends ListenerSignature<ActionSignature>, ErrorListener extends ErrorListenerSignature<Parameters<ActionSignature>>, BeforeActionListener extends BeforeActionSignature<ActionSignature>>(actionSignature: ActionSignature, listener?: Listener | null, errorListener?: ErrorListener | null, beforeActionListener?: BeforeActionListener | null): ReturnType<typeof createAction<ActionSignature>>;
|
package/dist/react/useAction.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const beforeActionListenerRef = (0, react_1.useRef)(beforeActionListener);
|
|
14
|
-
const action = (0, react_1.useMemo)(() => {
|
|
15
|
-
const action = (0, action_1.createAction)(actionSignature);
|
|
1
|
+
import { useContext, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { createAction } from "../action.js";
|
|
3
|
+
import { ErrorBoundaryContext } from "./ErrorBoundary.js";
|
|
4
|
+
export function useAction(actionSignature, listener, errorListener, beforeActionListener) {
|
|
5
|
+
const boundaryErrorListener = useContext(ErrorBoundaryContext);
|
|
6
|
+
const actionSignatureRef = useRef(actionSignature);
|
|
7
|
+
const listenerRef = useRef(listener);
|
|
8
|
+
const errorListenerRef = useRef(errorListener);
|
|
9
|
+
const boundaryErrorListenerRef = useRef(boundaryErrorListener);
|
|
10
|
+
const beforeActionListenerRef = useRef(beforeActionListener);
|
|
11
|
+
const action = useMemo(() => {
|
|
12
|
+
const action = createAction(actionSignature);
|
|
16
13
|
if (listenerRef.current) {
|
|
17
14
|
action.addListener(listenerRef.current);
|
|
18
15
|
}
|
|
@@ -27,13 +24,17 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
|
|
|
27
24
|
}
|
|
28
25
|
return action;
|
|
29
26
|
}, []);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
// Replace the action function in place when its reference changes,
|
|
28
|
+
// preserving all listeners and the action identity. A changed function
|
|
29
|
+
// must keep a compatible signature (TypeScript fixes the generic from the
|
|
30
|
+
// initial render).
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (actionSignatureRef.current !== actionSignature) {
|
|
33
|
+
action.setAction(actionSignature);
|
|
34
|
+
actionSignatureRef.current = actionSignature;
|
|
33
35
|
}
|
|
34
|
-
updateRef.current++;
|
|
35
36
|
}, [actionSignature]);
|
|
36
|
-
|
|
37
|
+
useEffect(() => {
|
|
37
38
|
if (listenerRef.current !== listener) {
|
|
38
39
|
if (listenerRef.current) {
|
|
39
40
|
action.removeListener(listenerRef.current);
|
|
@@ -44,7 +45,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
}, [listener]);
|
|
47
|
-
|
|
48
|
+
useEffect(() => {
|
|
48
49
|
if (errorListenerRef.current !== errorListener) {
|
|
49
50
|
if (errorListenerRef.current) {
|
|
50
51
|
action.removeErrorListener(errorListenerRef.current);
|
|
@@ -55,7 +56,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
|
|
|
55
56
|
}
|
|
56
57
|
}
|
|
57
58
|
}, [errorListener]);
|
|
58
|
-
|
|
59
|
+
useEffect(() => {
|
|
59
60
|
if (boundaryErrorListenerRef.current !== boundaryErrorListener) {
|
|
60
61
|
if (boundaryErrorListenerRef.current) {
|
|
61
62
|
action.removeErrorListener(boundaryErrorListenerRef.current);
|
|
@@ -66,7 +67,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
|
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
}, [boundaryErrorListener]);
|
|
69
|
-
|
|
70
|
+
useEffect(() => {
|
|
70
71
|
if (beforeActionListenerRef.current !== beforeActionListener) {
|
|
71
72
|
if (beforeActionListenerRef.current) {
|
|
72
73
|
action.removeBeforeActionListener(beforeActionListenerRef.current);
|
|
@@ -77,7 +78,7 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
|
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
80
|
}, [beforeActionListener]);
|
|
80
|
-
|
|
81
|
+
useEffect(() => {
|
|
81
82
|
return () => {
|
|
82
83
|
if (listenerRef.current) {
|
|
83
84
|
listenerRef.current = null;
|
|
@@ -94,7 +95,6 @@ function useAction(actionSignature, listener, errorListener, beforeActionListene
|
|
|
94
95
|
action.removeAllListeners();
|
|
95
96
|
action.removeAllBeforeActionListeners();
|
|
96
97
|
action.removeAllErrorListeners();
|
|
97
|
-
updateRef.current = 0;
|
|
98
98
|
};
|
|
99
99
|
}, []);
|
|
100
100
|
return action;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ActionResponse, ListenerSignature } from "../action";
|
|
2
|
-
import type { BaseActionsMap } from "../actionBus";
|
|
3
|
-
import { createActionBus } from "../actionBus";
|
|
4
|
-
import type { ErrorListenerSignature, ErrorResponse } from "../lib/types";
|
|
1
|
+
import type { ActionResponse, ListenerSignature } from "../action.js";
|
|
2
|
+
import type { BaseActionsMap } from "../actionBus.js";
|
|
3
|
+
import { createActionBus } from "../actionBus.js";
|
|
4
|
+
import type { ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
|
|
5
5
|
export type { ActionResponse, BaseActionsMap, ErrorListenerSignature, ErrorResponse, ListenerSignature, };
|
|
6
6
|
export declare function useActionBus<ActionsMap extends BaseActionsMap = BaseActionsMap>(initialActions?: ActionsMap, errorListener?: ErrorListenerSignature<any[]>): ReturnType<typeof createActionBus<ActionsMap>>;
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const boundaryErrorListenerRef = (0, react_1.useRef)(boundaryErrorListener);
|
|
11
|
-
const actionBus = (0, react_1.useMemo)(() => {
|
|
12
|
-
const actionBus = (0, actionBus_1.createActionBus)(initialActions);
|
|
1
|
+
import { useContext, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { createActionBus } from "../actionBus.js";
|
|
3
|
+
import { ErrorBoundaryContext } from "./ErrorBoundary.js";
|
|
4
|
+
export function useActionBus(initialActions, errorListener) {
|
|
5
|
+
const boundaryErrorListener = useContext(ErrorBoundaryContext);
|
|
6
|
+
const errorListenerRef = useRef(errorListener);
|
|
7
|
+
const boundaryErrorListenerRef = useRef(boundaryErrorListener);
|
|
8
|
+
const actionBus = useMemo(() => {
|
|
9
|
+
const actionBus = createActionBus(initialActions);
|
|
13
10
|
if (errorListener) {
|
|
14
11
|
actionBus.addErrorListener(errorListener);
|
|
15
12
|
}
|
|
@@ -18,7 +15,37 @@ function useActionBus(initialActions, errorListener) {
|
|
|
18
15
|
}
|
|
19
16
|
return actionBus;
|
|
20
17
|
}, []);
|
|
21
|
-
|
|
18
|
+
// Reconcile the actions map every render. Functions are compared by
|
|
19
|
+
// reference and never invoked.
|
|
20
|
+
const appliedActionsRef = useRef(Object.assign({}, initialActions));
|
|
21
|
+
const nextActions = (initialActions !== null && initialActions !== void 0 ? initialActions : {});
|
|
22
|
+
// Add newly-introduced actions during render (not in an effect): React runs
|
|
23
|
+
// child passive effects BEFORE parent passive effects, so a child rendered
|
|
24
|
+
// in the same pass that subscribes to a new action would otherwise throw
|
|
25
|
+
// "Action <name> not found". Parent render precedes child render, and
|
|
26
|
+
// add() is idempotent (a no-op if the action already exists).
|
|
27
|
+
for (const key in nextActions) {
|
|
28
|
+
actionBus.add(key, nextActions[key]);
|
|
29
|
+
}
|
|
30
|
+
// Replacements and removals can be deferred to a passive effect: a replaced
|
|
31
|
+
// action keeps its identity/listeners (so subscriptions are unaffected by
|
|
32
|
+
// timing), and removing late is harmless.
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const next = (initialActions !== null && initialActions !== void 0 ? initialActions : {});
|
|
35
|
+
const prev = appliedActionsRef.current;
|
|
36
|
+
for (const key in prev) {
|
|
37
|
+
if (!(key in next)) {
|
|
38
|
+
actionBus.removeAction(key);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
for (const key in next) {
|
|
42
|
+
if (key in prev && next[key] !== prev[key]) {
|
|
43
|
+
actionBus.replace(key, next[key]);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
appliedActionsRef.current = Object.assign({}, next);
|
|
47
|
+
});
|
|
48
|
+
useEffect(() => {
|
|
22
49
|
if (errorListenerRef.current !== errorListener) {
|
|
23
50
|
if (errorListenerRef.current) {
|
|
24
51
|
actionBus.removeErrorListener(errorListenerRef.current);
|
|
@@ -29,7 +56,7 @@ function useActionBus(initialActions, errorListener) {
|
|
|
29
56
|
}
|
|
30
57
|
}
|
|
31
58
|
}, [errorListener]);
|
|
32
|
-
|
|
59
|
+
useEffect(() => {
|
|
33
60
|
if (boundaryErrorListenerRef.current !== boundaryErrorListener) {
|
|
34
61
|
if (boundaryErrorListenerRef.current) {
|
|
35
62
|
actionBus.removeErrorListener(boundaryErrorListenerRef.current);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ActionResponse, ListenerSignature } from "../action";
|
|
2
|
-
import type { BaseActionsMap } from "../actionBus";
|
|
3
|
-
import { createActionMap } from "../actionMap";
|
|
4
|
-
import type { ErrorListenerSignature, ErrorResponse } from "../lib/types";
|
|
1
|
+
import type { ActionResponse, ListenerSignature } from "../action.js";
|
|
2
|
+
import type { BaseActionsMap } from "../actionBus.js";
|
|
3
|
+
import { createActionMap } from "../actionMap.js";
|
|
4
|
+
import type { ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
|
|
5
5
|
export type { ActionResponse, BaseActionsMap, ErrorListenerSignature, ErrorResponse, ListenerSignature, };
|
|
6
6
|
export declare function useActionMap<M extends BaseActionsMap>(actions: M, errorListener?: ErrorListenerSignature<any[]>): ReturnType<typeof createActionMap<M>>;
|
|
@@ -1,25 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const actionMap =
|
|
1
|
+
import { useContext, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { createActionMap } from "../actionMap.js";
|
|
3
|
+
import { ActionMapSetErrorListeners } from "../lib/actionMapInternal.js";
|
|
4
|
+
import { ErrorBoundaryContext } from "./ErrorBoundary.js";
|
|
5
|
+
export function useActionMap(actions, errorListener) {
|
|
6
|
+
const boundaryErrorListener = useContext(ErrorBoundaryContext);
|
|
7
|
+
const committedActionsRef = useRef(actions);
|
|
8
|
+
const committedErrorListenerRef = useRef(errorListener !== null && errorListener !== void 0 ? errorListener : null);
|
|
9
|
+
const committedBoundaryErrorListenerRef = useRef(boundaryErrorListener !== null && boundaryErrorListener !== void 0 ? boundaryErrorListener : null);
|
|
10
|
+
const actionMap = useMemo(() => {
|
|
11
11
|
const errorListeners = [
|
|
12
12
|
...(errorListener ? [errorListener] : []),
|
|
13
13
|
...(boundaryErrorListener ? [boundaryErrorListener] : []),
|
|
14
14
|
].filter(l => l !== undefined);
|
|
15
|
-
const actionMap =
|
|
15
|
+
const actionMap = createActionMap(actions, errorListeners);
|
|
16
16
|
return actionMap;
|
|
17
17
|
}, []);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
// The action map TYPE fixes the available keys, so the key set is static:
|
|
19
|
+
// reconcile values only (in-place setAction) and the forwarded error
|
|
20
|
+
// listeners. A runtime key-set change is a type-contract violation and
|
|
21
|
+
// keeps a defensive throw.
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const next = actions;
|
|
24
|
+
const prev = committedActionsRef.current;
|
|
25
|
+
const prevKeys = Object.keys(prev);
|
|
26
|
+
const nextKeys = Object.keys(next);
|
|
27
|
+
if (prevKeys.length !== nextKeys.length
|
|
28
|
+
|| nextKeys.some((key) => !(key in prev))) {
|
|
29
|
+
throw new Error("useActionMap() does not support changing the set of action keys");
|
|
21
30
|
}
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
for (const key of nextKeys) {
|
|
32
|
+
if (next[key] !== prev[key]) {
|
|
33
|
+
actionMap[key].setAction(next[key]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
committedActionsRef.current = next;
|
|
37
|
+
const nextErrorListener = errorListener !== null && errorListener !== void 0 ? errorListener : null;
|
|
38
|
+
const nextBoundaryErrorListener = boundaryErrorListener !== null && boundaryErrorListener !== void 0 ? boundaryErrorListener : null;
|
|
39
|
+
if (committedErrorListenerRef.current !== nextErrorListener
|
|
40
|
+
|| committedBoundaryErrorListenerRef.current
|
|
41
|
+
!== nextBoundaryErrorListener) {
|
|
42
|
+
const errorListeners = [
|
|
43
|
+
...(nextErrorListener ? [nextErrorListener] : []),
|
|
44
|
+
...(nextBoundaryErrorListener
|
|
45
|
+
? [nextBoundaryErrorListener]
|
|
46
|
+
: []),
|
|
47
|
+
];
|
|
48
|
+
actionMap[ActionMapSetErrorListeners](errorListeners);
|
|
49
|
+
committedErrorListenerRef.current = nextErrorListener;
|
|
50
|
+
committedBoundaryErrorListenerRef.current =
|
|
51
|
+
nextBoundaryErrorListener;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
24
54
|
return actionMap;
|
|
25
55
|
}
|
package/dist/react/useEvent.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createEvent, type EventOptions } from "../event";
|
|
2
|
-
import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types";
|
|
1
|
+
import { createEvent, type EventOptions } from "../event.js";
|
|
2
|
+
import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
|
|
3
3
|
export type { BaseHandler, ErrorListenerSignature, ErrorResponse, EventOptions, };
|
|
4
4
|
export declare function useEvent<Listener extends BaseHandler = BaseHandler, ErrorListener extends ErrorListenerSignature<Parameters<Listener>> = ErrorListenerSignature<Parameters<Listener>>>(eventOptions?: EventOptions<Listener>, listener?: Listener | null, errorListener?: ErrorListener | null): ReturnType<typeof createEvent<Listener>>;
|
package/dist/react/useEvent.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const event = (0, event_1.createEvent)(eventOptions);
|
|
1
|
+
import { useContext, useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { createEvent } from "../event.js";
|
|
3
|
+
import { normalizeEventOptions } from "../lib/normalizeEventOptions.js";
|
|
4
|
+
import { ErrorBoundaryContext } from "./ErrorBoundary.js";
|
|
5
|
+
import { areEventOptionsEqual } from "./listenerOptionsEqual.js";
|
|
6
|
+
export function useEvent(eventOptions = {}, listener, errorListener) {
|
|
7
|
+
const boundaryErrorListener = useContext(ErrorBoundaryContext);
|
|
8
|
+
const listenerRef = useRef(listener);
|
|
9
|
+
const errorListenerRef = useRef(errorListener);
|
|
10
|
+
const boundaryErrorListenerRef = useRef(boundaryErrorListener);
|
|
11
|
+
const event = useMemo(() => {
|
|
12
|
+
const event = createEvent(eventOptions);
|
|
14
13
|
if (listenerRef.current) {
|
|
15
14
|
event.addListener(listenerRef.current);
|
|
16
15
|
}
|
|
@@ -22,7 +21,21 @@ function useEvent(eventOptions = {}, listener, errorListener) {
|
|
|
22
21
|
}
|
|
23
22
|
return event;
|
|
24
23
|
}, []);
|
|
25
|
-
|
|
24
|
+
// Reconcile event options across renders without relying on object
|
|
25
|
+
// identity. Applied in place via setOptions; triggered count is preserved.
|
|
26
|
+
const committedEventOptionsRef = useRef(eventOptions);
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (committedEventOptionsRef.current === eventOptions) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (!areEventOptionsEqual(committedEventOptionsRef.current, eventOptions)) {
|
|
32
|
+
// Normalize so fields removed since the last render reset to their
|
|
33
|
+
// defaults (event.setOptions merges, it does not reset).
|
|
34
|
+
event.setOptions(normalizeEventOptions(eventOptions));
|
|
35
|
+
}
|
|
36
|
+
committedEventOptionsRef.current = eventOptions;
|
|
37
|
+
});
|
|
38
|
+
useEffect(() => {
|
|
26
39
|
if (listenerRef.current !== listener) {
|
|
27
40
|
if (listenerRef.current) {
|
|
28
41
|
event.removeListener(listenerRef.current);
|
|
@@ -33,7 +46,7 @@ function useEvent(eventOptions = {}, listener, errorListener) {
|
|
|
33
46
|
}
|
|
34
47
|
}
|
|
35
48
|
}, [listener]);
|
|
36
|
-
|
|
49
|
+
useEffect(() => {
|
|
37
50
|
if (errorListenerRef.current !== errorListener) {
|
|
38
51
|
if (errorListenerRef.current) {
|
|
39
52
|
event.removeErrorListener(errorListenerRef.current);
|
|
@@ -44,7 +57,7 @@ function useEvent(eventOptions = {}, listener, errorListener) {
|
|
|
44
57
|
}
|
|
45
58
|
}
|
|
46
59
|
}, [errorListener]);
|
|
47
|
-
|
|
60
|
+
useEffect(() => {
|
|
48
61
|
if (boundaryErrorListenerRef.current !== boundaryErrorListener) {
|
|
49
62
|
if (boundaryErrorListenerRef.current) {
|
|
50
63
|
event.removeErrorListener(boundaryErrorListenerRef.current);
|
|
@@ -55,7 +68,7 @@ function useEvent(eventOptions = {}, listener, errorListener) {
|
|
|
55
68
|
}
|
|
56
69
|
}
|
|
57
70
|
}, [boundaryErrorListener]);
|
|
58
|
-
|
|
71
|
+
useEffect(() => {
|
|
59
72
|
return () => {
|
|
60
73
|
if (listenerRef.current) {
|
|
61
74
|
event.removeListener(listenerRef.current);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseEventMap, createEventBus, DefaultEventMap, EventBusOptions } from "../eventBus";
|
|
2
|
-
import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types";
|
|
1
|
+
import { BaseEventMap, createEventBus, DefaultEventMap, EventBusOptions } from "../eventBus.js";
|
|
2
|
+
import type { BaseHandler, ErrorListenerSignature, ErrorResponse } from "../lib/types.js";
|
|
3
3
|
export type { BaseEventMap, BaseHandler, ErrorListenerSignature, ErrorResponse, EventBusOptions, };
|
|
4
4
|
export declare function useEventBus<EventsMap extends BaseEventMap = DefaultEventMap>(eventBusOptions?: EventBusOptions<EventsMap>, allEventsListener?: BaseHandler, errorListener?: ErrorListenerSignature<any[]>): ReturnType<typeof createEventBus<EventsMap>>;
|