@nice-code/state 0.7.0 → 0.9.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/build/Store-B65MojT2.d.ts +201 -0
- package/build/Store-CI9N0P6I.js +366 -0
- package/build/Store-CI9N0P6I.js.map +1 -0
- package/build/Store-PjfFkZ2I.js +349 -0
- package/build/Store-PjfFkZ2I.js.map +1 -0
- package/build/devtools/browser/index.d.ts +120 -0
- package/build/devtools/browser/index.js +2750 -2357
- package/build/devtools/browser/index.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +2 -244
- package/build/react/index.d.ts +58 -0
- package/build/react/index.js +59 -308
- package/build/react/index.js.map +1 -0
- package/package.json +29 -26
- package/build/types/core/Store.d.ts +0 -136
- package/build/types/core/index.d.ts +0 -1
- package/build/types/devtools/browser/NiceStateDevtools.d.ts +0 -10
- package/build/types/devtools/browser/components/ChangeDetailPanel.d.ts +0 -12
- package/build/types/devtools/browser/components/ChangeList.d.ts +0 -9
- package/build/types/devtools/browser/components/DiffView.d.ts +0 -13
- package/build/types/devtools/browser/components/JsonDiffView.d.ts +0 -24
- package/build/types/devtools/browser/components/JsonView.d.ts +0 -7
- package/build/types/devtools/browser/components/PanelChrome.d.ts +0 -54
- package/build/types/devtools/browser/components/SectionLabel.d.ts +0 -4
- package/build/types/devtools/browser/components/StateInspector.d.ts +0 -16
- package/build/types/devtools/browser/components/StoreTabs.d.ts +0 -12
- package/build/types/devtools/browser/components/utils.d.ts +0 -98
- package/build/types/devtools/browser/devtools_dock.d.ts +0 -54
- package/build/types/devtools/browser/index.d.ts +0 -3
- package/build/types/devtools/core/StateDevtools.types.d.ts +0 -43
- package/build/types/devtools/core/StateDevtoolsCore.d.ts +0 -56
- package/build/types/devtools/core/devtools_colors.d.ts +0 -26
- package/build/types/index.d.ts +0 -1
- package/build/types/react/InjectStoreState.d.ts +0 -18
- package/build/types/react/index.d.ts +0 -3
- package/build/types/react/useLocalStore.d.ts +0 -8
- package/build/types/react/useStoreState.d.ts +0 -23
package/build/index.js
CHANGED
|
@@ -1,244 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
applyPatches,
|
|
5
|
-
enablePatches,
|
|
6
|
-
produce,
|
|
7
|
-
produceWithPatches
|
|
8
|
-
} from "immer";
|
|
9
|
-
enablePatches();
|
|
10
|
-
function makeSubscriptionFunction(store, watch, listener) {
|
|
11
|
-
let lastWatchState = watch(store.getRawState());
|
|
12
|
-
return () => {
|
|
13
|
-
const currentState = store.getRawState();
|
|
14
|
-
const nextWatchState = watch(currentState);
|
|
15
|
-
if (nextWatchState === lastWatchState) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
if (deepEqual(nextWatchState, lastWatchState)) {
|
|
19
|
-
lastWatchState = nextWatchState;
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
const previousWatched = lastWatchState;
|
|
23
|
-
lastWatchState = nextWatchState;
|
|
24
|
-
listener(nextWatchState, currentState, previousWatched);
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
function makeReactionFunctionCreator(watch, reaction) {
|
|
28
|
-
return (store) => {
|
|
29
|
-
let lastWatchState = watch(store.getRawState());
|
|
30
|
-
return (forceRun = false) => {
|
|
31
|
-
const currentState = store.getRawState();
|
|
32
|
-
const nextWatchState = watch(currentState);
|
|
33
|
-
if (!forceRun) {
|
|
34
|
-
if (nextWatchState === lastWatchState) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (deepEqual(nextWatchState, lastWatchState)) {
|
|
38
|
-
lastWatchState = nextWatchState;
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
const previousWatched = lastWatchState;
|
|
43
|
-
lastWatchState = nextWatchState;
|
|
44
|
-
if (store._hasPatchListeners()) {
|
|
45
|
-
const [nextState, patches, inversePatches] = produceWithPatches(currentState, (draft) => {
|
|
46
|
-
reaction(nextWatchState, draft, currentState, previousWatched);
|
|
47
|
-
});
|
|
48
|
-
store._updateStateWithoutReaction(nextState);
|
|
49
|
-
if (patches.length > 0) {
|
|
50
|
-
store._emitPatches(patches, inversePatches);
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
const nextState = produce(currentState, (draft) => {
|
|
54
|
-
reaction(nextWatchState, draft, currentState, previousWatched);
|
|
55
|
-
});
|
|
56
|
-
store._updateStateWithoutReaction(nextState);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
class Store {
|
|
63
|
-
currentState;
|
|
64
|
-
createInitialState;
|
|
65
|
-
ssr = false;
|
|
66
|
-
listeners = new Set;
|
|
67
|
-
reactionCreators = [];
|
|
68
|
-
reactions = [];
|
|
69
|
-
clientSubscriptions = [];
|
|
70
|
-
_patchListeners = [];
|
|
71
|
-
constructor(initialState) {
|
|
72
|
-
if (initialState instanceof Function) {
|
|
73
|
-
this.createInitialState = initialState;
|
|
74
|
-
this.currentState = initialState();
|
|
75
|
-
} else {
|
|
76
|
-
this.createInitialState = () => initialState;
|
|
77
|
-
this.currentState = initialState;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
get state() {
|
|
81
|
-
return Object.freeze(this.currentState);
|
|
82
|
-
}
|
|
83
|
-
_setInternalOptions({ ssr, reactionCreators = [] }) {
|
|
84
|
-
this.ssr = ssr;
|
|
85
|
-
this.reactionCreators = reactionCreators;
|
|
86
|
-
this.reactions = reactionCreators.map((rc) => rc(this));
|
|
87
|
-
}
|
|
88
|
-
_getReactionCreators() {
|
|
89
|
-
return this.reactionCreators;
|
|
90
|
-
}
|
|
91
|
-
_instantiateReactions() {
|
|
92
|
-
this.reactions = this.reactionCreators.map((rc) => rc(this));
|
|
93
|
-
}
|
|
94
|
-
_getInitialState() {
|
|
95
|
-
return this.createInitialState();
|
|
96
|
-
}
|
|
97
|
-
_hasPatchListeners() {
|
|
98
|
-
return this._patchListeners.length > 0;
|
|
99
|
-
}
|
|
100
|
-
_emitPatches(patches, inversePatches) {
|
|
101
|
-
for (const listener of this._patchListeners) {
|
|
102
|
-
listener(patches, inversePatches);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
_updateStateWithoutReaction(nextState) {
|
|
106
|
-
this.currentState = nextState;
|
|
107
|
-
}
|
|
108
|
-
_updateState(nextState) {
|
|
109
|
-
this.currentState = nextState;
|
|
110
|
-
for (const runReaction of this.reactions) {
|
|
111
|
-
runReaction();
|
|
112
|
-
}
|
|
113
|
-
if (this.ssr) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
for (const runSubscription of this.clientSubscriptions) {
|
|
117
|
-
runSubscription();
|
|
118
|
-
}
|
|
119
|
-
for (const listener of this.listeners) {
|
|
120
|
-
listener();
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
getRawState() {
|
|
124
|
-
return this.currentState;
|
|
125
|
-
}
|
|
126
|
-
subscribe(listener) {
|
|
127
|
-
this.listeners.add(listener);
|
|
128
|
-
return () => {
|
|
129
|
-
this.listeners.delete(listener);
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
watch(watch, listener) {
|
|
133
|
-
if (this.ssr) {
|
|
134
|
-
return () => {
|
|
135
|
-
console.warn(`@nice-code/state: Subscriptions made on the server side are not registered, so this unsubscribe call does nothing.`);
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
const run = makeSubscriptionFunction(this, watch, listener);
|
|
139
|
-
this.clientSubscriptions.push(run);
|
|
140
|
-
return () => {
|
|
141
|
-
this.clientSubscriptions = this.clientSubscriptions.filter((f) => f !== run);
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
createReaction(watch, reaction, { runNow = false, runNowWithSideEffects = false } = {}) {
|
|
145
|
-
const creator = makeReactionFunctionCreator(watch, reaction);
|
|
146
|
-
this.reactionCreators.push(creator);
|
|
147
|
-
const run = creator(this);
|
|
148
|
-
this.reactions.push(run);
|
|
149
|
-
if (runNow || runNowWithSideEffects) {
|
|
150
|
-
run(true);
|
|
151
|
-
if (runNowWithSideEffects && !this.ssr) {
|
|
152
|
-
this._updateState(this.currentState);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return () => {
|
|
156
|
-
this.reactions = this.reactions.filter((f) => f !== run);
|
|
157
|
-
this.reactionCreators = this.reactionCreators.filter((f) => f !== creator);
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
listenToPatches(patchListener) {
|
|
161
|
-
this._patchListeners.push(patchListener);
|
|
162
|
-
return () => {
|
|
163
|
-
this._patchListeners = this._patchListeners.filter((f) => f !== patchListener);
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
update(updater, patchesCallback) {
|
|
167
|
-
update(this, updater, patchesCallback);
|
|
168
|
-
}
|
|
169
|
-
replace(newState) {
|
|
170
|
-
if (newState !== this.currentState) {
|
|
171
|
-
this._updateState(newState);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
replaceFromCurrent(replacer) {
|
|
175
|
-
const nextState = replacer(this.currentState);
|
|
176
|
-
if (nextState !== this.currentState) {
|
|
177
|
-
this._updateState(nextState);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
applyPatches(patches) {
|
|
181
|
-
applyPatchesToStore(this, patches);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
function applyPatchesToStore(store, patches) {
|
|
185
|
-
const currentState = store.getRawState();
|
|
186
|
-
const nextState = applyPatches(currentState, patches);
|
|
187
|
-
if (nextState !== currentState) {
|
|
188
|
-
store._updateState(nextState);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
function applyUpdaterWithPatches(currentState, updater) {
|
|
192
|
-
if (typeof updater === "function") {
|
|
193
|
-
const [next, patches2, inversePatches2] = produceWithPatches(currentState, (draft) => {
|
|
194
|
-
updater(draft, currentState);
|
|
195
|
-
});
|
|
196
|
-
return [next, patches2, inversePatches2];
|
|
197
|
-
}
|
|
198
|
-
let state = currentState;
|
|
199
|
-
const patches = [];
|
|
200
|
-
const inversePatches = [];
|
|
201
|
-
for (const single of updater) {
|
|
202
|
-
const [next, p, ip] = produceWithPatches(state, (draft) => {
|
|
203
|
-
single(draft, state);
|
|
204
|
-
});
|
|
205
|
-
state = next;
|
|
206
|
-
patches.push(...p);
|
|
207
|
-
inversePatches.push(...ip);
|
|
208
|
-
}
|
|
209
|
-
return [state, patches, inversePatches];
|
|
210
|
-
}
|
|
211
|
-
function applyUpdater(currentState, updater) {
|
|
212
|
-
if (typeof updater === "function") {
|
|
213
|
-
return produce(currentState, (draft) => {
|
|
214
|
-
updater(draft, currentState);
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
return updater.reduce((previousState, single) => produce(previousState, (draft) => {
|
|
218
|
-
single(draft, previousState);
|
|
219
|
-
}), currentState);
|
|
220
|
-
}
|
|
221
|
-
function update(store, updater, patchesCallback) {
|
|
222
|
-
const currentState = store.getRawState();
|
|
223
|
-
let nextState;
|
|
224
|
-
if (store._hasPatchListeners() || patchesCallback != null) {
|
|
225
|
-
const [next, patches, inversePatches] = applyUpdaterWithPatches(currentState, updater);
|
|
226
|
-
if (patches.length > 0) {
|
|
227
|
-
if (patchesCallback != null) {
|
|
228
|
-
patchesCallback(patches, inversePatches);
|
|
229
|
-
}
|
|
230
|
-
store._emitPatches(patches, inversePatches);
|
|
231
|
-
}
|
|
232
|
-
nextState = next;
|
|
233
|
-
} else {
|
|
234
|
-
nextState = applyUpdater(currentState, updater);
|
|
235
|
-
}
|
|
236
|
-
if (nextState !== currentState) {
|
|
237
|
-
store._updateState(nextState);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
export {
|
|
241
|
-
update,
|
|
242
|
-
applyPatchesToStore,
|
|
243
|
-
Store
|
|
244
|
-
};
|
|
1
|
+
import { n as applyPatchesToStore, r as update, t as Store } from "./Store-PjfFkZ2I.js";
|
|
2
|
+
export { Store, applyPatchesToStore, update };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { r as Store } from "../Store-B65MojT2.js";
|
|
2
|
+
import { ReactElement } from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/react/useStoreState.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Compares the previously selected value against the next one. Returning `true`
|
|
7
|
+
* tells the hook the value is unchanged, so React is handed the *same*
|
|
8
|
+
* reference and no re-render is scheduled.
|
|
9
|
+
*/
|
|
10
|
+
type TEqualityFn<SS> = (a: SS, b: SS) => boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Subscribe a React component to a store, optionally narrowing to a derived
|
|
13
|
+
* slice.
|
|
14
|
+
*
|
|
15
|
+
* Built on `useSyncExternalStore`, so it is tear-free under React 18+
|
|
16
|
+
* concurrent rendering with no manual `useState`/`useEffect` subscription loop.
|
|
17
|
+
*
|
|
18
|
+
* A `useRef` cache holds the last selected value. On every store emission the
|
|
19
|
+
* selector is re-evaluated and the result is run through a strict-reference
|
|
20
|
+
* fast-path first; only if the reference differs is `equalityFn` consulted. A
|
|
21
|
+
* new reference is handed back to React exclusively when a genuine change is
|
|
22
|
+
* detected, so equal-but-new selector results never trigger a render.
|
|
23
|
+
*/
|
|
24
|
+
declare function useStoreState<S extends object>(store: Store<S>): S;
|
|
25
|
+
declare function useStoreState<S extends object, SS>(store: Store<S>, getSubState: (state: S) => SS, equalityFn?: TEqualityFn<SS>): SS;
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/react/InjectStoreState.d.ts
|
|
28
|
+
interface IPropsInjectStoreState<S extends object, SS> {
|
|
29
|
+
store: Store<S>;
|
|
30
|
+
on?: (state: S) => SS;
|
|
31
|
+
/**
|
|
32
|
+
* Optional equality function for the selected slice. Defaults to a strict
|
|
33
|
+
* reference check; pass `deepEqual` from `fast-equals` for inline objects.
|
|
34
|
+
*/
|
|
35
|
+
equalityFn?: TEqualityFn<SS>;
|
|
36
|
+
children: (output: SS) => ReactElement;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Render-prop binding: subscribes to `store` (optionally narrowed by `on`) and
|
|
40
|
+
* renders `children` with the selected slice.
|
|
41
|
+
*/
|
|
42
|
+
declare function InjectStoreState<S extends object, SS = S>({
|
|
43
|
+
store,
|
|
44
|
+
on,
|
|
45
|
+
equalityFn,
|
|
46
|
+
children
|
|
47
|
+
}: IPropsInjectStoreState<S, SS>): ReactElement;
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/react/useLocalStore.d.ts
|
|
50
|
+
/**
|
|
51
|
+
* Create a component-local {@link Store} that persists across renders. Passing a
|
|
52
|
+
* `deps` array re-creates the store (with a fresh initial state) whenever the
|
|
53
|
+
* dependencies change by shallow comparison.
|
|
54
|
+
*/
|
|
55
|
+
declare function useLocalStore<S extends object>(initialState: (() => S) | S, deps?: ReadonlyArray<unknown>): Store<S>;
|
|
56
|
+
//#endregion
|
|
57
|
+
export { type IPropsInjectStoreState, InjectStoreState, type TEqualityFn, useLocalStore, useStoreState };
|
|
58
|
+
//# sourceMappingURL=index.d.ts.map
|
package/build/react/index.js
CHANGED
|
@@ -1,318 +1,69 @@
|
|
|
1
|
-
|
|
1
|
+
import { t as Store } from "../Store-PjfFkZ2I.js";
|
|
2
2
|
import { useCallback, useRef, useSyncExternalStore } from "react";
|
|
3
|
+
//#region src/react/useStoreState.ts
|
|
3
4
|
function strictEqual(a, b) {
|
|
4
|
-
|
|
5
|
+
return a === b;
|
|
5
6
|
}
|
|
6
7
|
function useStoreState(store, getSubState, equalityFn = strictEqual) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
cache.value = nextValue;
|
|
29
|
-
return nextValue;
|
|
30
|
-
}, [store]);
|
|
31
|
-
const subscribe = useCallback((onStoreChange) => store.subscribe(onStoreChange), [store]);
|
|
32
|
-
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
8
|
+
const selectorRef = useRef(getSubState);
|
|
9
|
+
const equalityRef = useRef(equalityFn);
|
|
10
|
+
selectorRef.current = getSubState;
|
|
11
|
+
equalityRef.current = equalityFn;
|
|
12
|
+
const cacheRef = useRef(null);
|
|
13
|
+
const getSnapshot = useCallback(() => {
|
|
14
|
+
const rawState = store.getRawState();
|
|
15
|
+
const selector = selectorRef.current;
|
|
16
|
+
const nextValue = selector ? selector(rawState) : rawState;
|
|
17
|
+
const cache = cacheRef.current;
|
|
18
|
+
if (cache === null) {
|
|
19
|
+
cacheRef.current = { value: nextValue };
|
|
20
|
+
return nextValue;
|
|
21
|
+
}
|
|
22
|
+
const lastValue = cache.value;
|
|
23
|
+
if (nextValue === lastValue) return lastValue;
|
|
24
|
+
if (equalityRef.current(lastValue, nextValue)) return lastValue;
|
|
25
|
+
cache.value = nextValue;
|
|
26
|
+
return nextValue;
|
|
27
|
+
}, [store]);
|
|
28
|
+
return useSyncExternalStore(useCallback((onStoreChange) => store.subscribe(onStoreChange), [store]), getSnapshot, getSnapshot);
|
|
33
29
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const state = useStoreState(store, on ?? ((s) => s), equalityFn);
|
|
43
|
-
return children(state);
|
|
44
|
-
}
|
|
45
|
-
// src/react/useLocalStore.ts
|
|
46
|
-
import { useRef as useRef2 } from "react";
|
|
47
|
-
|
|
48
|
-
// src/core/Store.ts
|
|
49
|
-
import { deepEqual } from "fast-equals";
|
|
50
|
-
import {
|
|
51
|
-
applyPatches,
|
|
52
|
-
enablePatches,
|
|
53
|
-
produce,
|
|
54
|
-
produceWithPatches
|
|
55
|
-
} from "immer";
|
|
56
|
-
enablePatches();
|
|
57
|
-
function makeSubscriptionFunction(store, watch, listener) {
|
|
58
|
-
let lastWatchState = watch(store.getRawState());
|
|
59
|
-
return () => {
|
|
60
|
-
const currentState = store.getRawState();
|
|
61
|
-
const nextWatchState = watch(currentState);
|
|
62
|
-
if (nextWatchState === lastWatchState) {
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
if (deepEqual(nextWatchState, lastWatchState)) {
|
|
66
|
-
lastWatchState = nextWatchState;
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
69
|
-
const previousWatched = lastWatchState;
|
|
70
|
-
lastWatchState = nextWatchState;
|
|
71
|
-
listener(nextWatchState, currentState, previousWatched);
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function makeReactionFunctionCreator(watch, reaction) {
|
|
75
|
-
return (store) => {
|
|
76
|
-
let lastWatchState = watch(store.getRawState());
|
|
77
|
-
return (forceRun = false) => {
|
|
78
|
-
const currentState = store.getRawState();
|
|
79
|
-
const nextWatchState = watch(currentState);
|
|
80
|
-
if (!forceRun) {
|
|
81
|
-
if (nextWatchState === lastWatchState) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
if (deepEqual(nextWatchState, lastWatchState)) {
|
|
85
|
-
lastWatchState = nextWatchState;
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
const previousWatched = lastWatchState;
|
|
90
|
-
lastWatchState = nextWatchState;
|
|
91
|
-
if (store._hasPatchListeners()) {
|
|
92
|
-
const [nextState, patches, inversePatches] = produceWithPatches(currentState, (draft) => {
|
|
93
|
-
reaction(nextWatchState, draft, currentState, previousWatched);
|
|
94
|
-
});
|
|
95
|
-
store._updateStateWithoutReaction(nextState);
|
|
96
|
-
if (patches.length > 0) {
|
|
97
|
-
store._emitPatches(patches, inversePatches);
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
const nextState = produce(currentState, (draft) => {
|
|
101
|
-
reaction(nextWatchState, draft, currentState, previousWatched);
|
|
102
|
-
});
|
|
103
|
-
store._updateStateWithoutReaction(nextState);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
class Store {
|
|
110
|
-
currentState;
|
|
111
|
-
createInitialState;
|
|
112
|
-
ssr = false;
|
|
113
|
-
listeners = new Set;
|
|
114
|
-
reactionCreators = [];
|
|
115
|
-
reactions = [];
|
|
116
|
-
clientSubscriptions = [];
|
|
117
|
-
_patchListeners = [];
|
|
118
|
-
constructor(initialState) {
|
|
119
|
-
if (initialState instanceof Function) {
|
|
120
|
-
this.createInitialState = initialState;
|
|
121
|
-
this.currentState = initialState();
|
|
122
|
-
} else {
|
|
123
|
-
this.createInitialState = () => initialState;
|
|
124
|
-
this.currentState = initialState;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
get state() {
|
|
128
|
-
return Object.freeze(this.currentState);
|
|
129
|
-
}
|
|
130
|
-
_setInternalOptions({ ssr, reactionCreators = [] }) {
|
|
131
|
-
this.ssr = ssr;
|
|
132
|
-
this.reactionCreators = reactionCreators;
|
|
133
|
-
this.reactions = reactionCreators.map((rc) => rc(this));
|
|
134
|
-
}
|
|
135
|
-
_getReactionCreators() {
|
|
136
|
-
return this.reactionCreators;
|
|
137
|
-
}
|
|
138
|
-
_instantiateReactions() {
|
|
139
|
-
this.reactions = this.reactionCreators.map((rc) => rc(this));
|
|
140
|
-
}
|
|
141
|
-
_getInitialState() {
|
|
142
|
-
return this.createInitialState();
|
|
143
|
-
}
|
|
144
|
-
_hasPatchListeners() {
|
|
145
|
-
return this._patchListeners.length > 0;
|
|
146
|
-
}
|
|
147
|
-
_emitPatches(patches, inversePatches) {
|
|
148
|
-
for (const listener of this._patchListeners) {
|
|
149
|
-
listener(patches, inversePatches);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
_updateStateWithoutReaction(nextState) {
|
|
153
|
-
this.currentState = nextState;
|
|
154
|
-
}
|
|
155
|
-
_updateState(nextState) {
|
|
156
|
-
this.currentState = nextState;
|
|
157
|
-
for (const runReaction of this.reactions) {
|
|
158
|
-
runReaction();
|
|
159
|
-
}
|
|
160
|
-
if (this.ssr) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
for (const runSubscription of this.clientSubscriptions) {
|
|
164
|
-
runSubscription();
|
|
165
|
-
}
|
|
166
|
-
for (const listener of this.listeners) {
|
|
167
|
-
listener();
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
getRawState() {
|
|
171
|
-
return this.currentState;
|
|
172
|
-
}
|
|
173
|
-
subscribe(listener) {
|
|
174
|
-
this.listeners.add(listener);
|
|
175
|
-
return () => {
|
|
176
|
-
this.listeners.delete(listener);
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
watch(watch, listener) {
|
|
180
|
-
if (this.ssr) {
|
|
181
|
-
return () => {
|
|
182
|
-
console.warn(`@nice-code/state: Subscriptions made on the server side are not registered, so this unsubscribe call does nothing.`);
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
const run = makeSubscriptionFunction(this, watch, listener);
|
|
186
|
-
this.clientSubscriptions.push(run);
|
|
187
|
-
return () => {
|
|
188
|
-
this.clientSubscriptions = this.clientSubscriptions.filter((f) => f !== run);
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
createReaction(watch, reaction, { runNow = false, runNowWithSideEffects = false } = {}) {
|
|
192
|
-
const creator = makeReactionFunctionCreator(watch, reaction);
|
|
193
|
-
this.reactionCreators.push(creator);
|
|
194
|
-
const run = creator(this);
|
|
195
|
-
this.reactions.push(run);
|
|
196
|
-
if (runNow || runNowWithSideEffects) {
|
|
197
|
-
run(true);
|
|
198
|
-
if (runNowWithSideEffects && !this.ssr) {
|
|
199
|
-
this._updateState(this.currentState);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return () => {
|
|
203
|
-
this.reactions = this.reactions.filter((f) => f !== run);
|
|
204
|
-
this.reactionCreators = this.reactionCreators.filter((f) => f !== creator);
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
listenToPatches(patchListener) {
|
|
208
|
-
this._patchListeners.push(patchListener);
|
|
209
|
-
return () => {
|
|
210
|
-
this._patchListeners = this._patchListeners.filter((f) => f !== patchListener);
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
update(updater, patchesCallback) {
|
|
214
|
-
update(this, updater, patchesCallback);
|
|
215
|
-
}
|
|
216
|
-
replace(newState) {
|
|
217
|
-
if (newState !== this.currentState) {
|
|
218
|
-
this._updateState(newState);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
replaceFromCurrent(replacer) {
|
|
222
|
-
const nextState = replacer(this.currentState);
|
|
223
|
-
if (nextState !== this.currentState) {
|
|
224
|
-
this._updateState(nextState);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
applyPatches(patches) {
|
|
228
|
-
applyPatchesToStore(this, patches);
|
|
229
|
-
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/react/InjectStoreState.ts
|
|
32
|
+
/**
|
|
33
|
+
* Render-prop binding: subscribes to `store` (optionally narrowed by `on`) and
|
|
34
|
+
* renders `children` with the selected slice.
|
|
35
|
+
*/
|
|
36
|
+
function InjectStoreState({ store, on, equalityFn, children }) {
|
|
37
|
+
return children(useStoreState(store, on ?? ((s) => s), equalityFn));
|
|
230
38
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
function applyUpdaterWithPatches(currentState, updater) {
|
|
239
|
-
if (typeof updater === "function") {
|
|
240
|
-
const [next, patches2, inversePatches2] = produceWithPatches(currentState, (draft) => {
|
|
241
|
-
updater(draft, currentState);
|
|
242
|
-
});
|
|
243
|
-
return [next, patches2, inversePatches2];
|
|
244
|
-
}
|
|
245
|
-
let state = currentState;
|
|
246
|
-
const patches = [];
|
|
247
|
-
const inversePatches = [];
|
|
248
|
-
for (const single of updater) {
|
|
249
|
-
const [next, p, ip] = produceWithPatches(state, (draft) => {
|
|
250
|
-
single(draft, state);
|
|
251
|
-
});
|
|
252
|
-
state = next;
|
|
253
|
-
patches.push(...p);
|
|
254
|
-
inversePatches.push(...ip);
|
|
255
|
-
}
|
|
256
|
-
return [state, patches, inversePatches];
|
|
257
|
-
}
|
|
258
|
-
function applyUpdater(currentState, updater) {
|
|
259
|
-
if (typeof updater === "function") {
|
|
260
|
-
return produce(currentState, (draft) => {
|
|
261
|
-
updater(draft, currentState);
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
return updater.reduce((previousState, single) => produce(previousState, (draft) => {
|
|
265
|
-
single(draft, previousState);
|
|
266
|
-
}), currentState);
|
|
267
|
-
}
|
|
268
|
-
function update(store, updater, patchesCallback) {
|
|
269
|
-
const currentState = store.getRawState();
|
|
270
|
-
let nextState;
|
|
271
|
-
if (store._hasPatchListeners() || patchesCallback != null) {
|
|
272
|
-
const [next, patches, inversePatches] = applyUpdaterWithPatches(currentState, updater);
|
|
273
|
-
if (patches.length > 0) {
|
|
274
|
-
if (patchesCallback != null) {
|
|
275
|
-
patchesCallback(patches, inversePatches);
|
|
276
|
-
}
|
|
277
|
-
store._emitPatches(patches, inversePatches);
|
|
278
|
-
}
|
|
279
|
-
nextState = next;
|
|
280
|
-
} else {
|
|
281
|
-
nextState = applyUpdater(currentState, updater);
|
|
282
|
-
}
|
|
283
|
-
if (nextState !== currentState) {
|
|
284
|
-
store._updateState(nextState);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// src/react/useLocalStore.ts
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/react/useLocalStore.ts
|
|
41
|
+
/**
|
|
42
|
+
* Standard shallow comparison of dependency arrays — the same semantics React
|
|
43
|
+
* uses for its own hook dependency lists. No deep traversal.
|
|
44
|
+
*/
|
|
289
45
|
function shallowEqualDeps(a, b) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
return false;
|
|
295
|
-
}
|
|
296
|
-
for (let i = 0;i < a.length; i++) {
|
|
297
|
-
if (a[i] !== b[i]) {
|
|
298
|
-
return false;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return true;
|
|
46
|
+
if (a === b) return true;
|
|
47
|
+
if (a == null || b == null || a.length !== b.length) return false;
|
|
48
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
49
|
+
return true;
|
|
302
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Create a component-local {@link Store} that persists across renders. Passing a
|
|
53
|
+
* `deps` array re-creates the store (with a fresh initial state) whenever the
|
|
54
|
+
* dependencies change by shallow comparison.
|
|
55
|
+
*/
|
|
303
56
|
function useLocalStore(initialState, deps) {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
return storeRef.current;
|
|
57
|
+
const storeRef = useRef(null);
|
|
58
|
+
const depsRef = useRef(deps);
|
|
59
|
+
if (storeRef.current === null) storeRef.current = new Store(initialState);
|
|
60
|
+
else if (deps !== void 0 && !shallowEqualDeps(depsRef.current, deps)) {
|
|
61
|
+
storeRef.current = new Store(initialState);
|
|
62
|
+
depsRef.current = deps;
|
|
63
|
+
}
|
|
64
|
+
return storeRef.current;
|
|
313
65
|
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
};
|
|
66
|
+
//#endregion
|
|
67
|
+
export { InjectStoreState, useLocalStore, useStoreState };
|
|
68
|
+
|
|
69
|
+
//# sourceMappingURL=index.js.map
|