@legendapp/state 3.0.0-beta.46 → 3.0.0-beta.48

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/react.js CHANGED
@@ -13,8 +13,17 @@ var React__default = /*#__PURE__*/_interopDefault(React);
13
13
 
14
14
  // src/react/react-globals.ts
15
15
  var reactGlobals = {
16
- inObserver: false
16
+ inObserver: false,
17
+ renderDepth: 0
17
18
  };
19
+ function runInRender(fn) {
20
+ reactGlobals.renderDepth++;
21
+ try {
22
+ return fn();
23
+ } finally {
24
+ reactGlobals.renderDepth--;
25
+ }
26
+ }
18
27
  var pauseContext = void 0;
19
28
  var getPauseContext = () => {
20
29
  return pauseContext || (pauseContext = React.createContext(null));
@@ -36,6 +45,22 @@ function createSelectorFunctions(options, isPaused$) {
36
45
  let _selector;
37
46
  let prev;
38
47
  let pendingUpdate = void 0;
48
+ let notifyQueued = false;
49
+ const scheduleNotify = () => {
50
+ if (notify) {
51
+ if (reactGlobals.renderDepth > 0) {
52
+ if (!notifyQueued) {
53
+ notifyQueued = true;
54
+ queueMicrotask(() => {
55
+ notifyQueued = false;
56
+ notify == null ? void 0 : notify();
57
+ });
58
+ }
59
+ } else {
60
+ notify();
61
+ }
62
+ }
63
+ };
39
64
  const run = () => {
40
65
  dispose == null ? void 0 : dispose();
41
66
  const {
@@ -79,7 +104,7 @@ function createSelectorFunctions(options, isPaused$) {
79
104
  }
80
105
  if (changed) {
81
106
  version++;
82
- notify == null ? void 0 : notify();
107
+ scheduleNotify();
83
108
  }
84
109
  }
85
110
  };
@@ -91,6 +116,7 @@ function createSelectorFunctions(options, isPaused$) {
91
116
  }
92
117
  return () => {
93
118
  dispose == null ? void 0 : dispose();
119
+ notify = void 0;
94
120
  dispose = void 0;
95
121
  };
96
122
  },
@@ -123,7 +149,7 @@ function useSelector(selector, options) {
123
149
  const isPaused$ = React.useContext(getPauseContext());
124
150
  const selectorFn = React.useMemo(() => createSelectorFunctions(options, isPaused$), []);
125
151
  const { subscribe, getVersion, run } = selectorFn;
126
- value = run(selector);
152
+ value = runInRender(() => run(selector));
127
153
  index_js.useSyncExternalStore(subscribe, getVersion, getVersion);
128
154
  } catch (err) {
129
155
  if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.includes("Rendered more"))) {
@@ -175,50 +201,64 @@ function createReactiveComponent(component, observe3, reactive2, keysReactive, b
175
201
  apply(target, thisArg, argArray) {
176
202
  if (reactive2) {
177
203
  const props = argArray[0];
178
- const propsOut = {};
179
- const keys = Object.keys(props);
180
- for (let i = 0; i < keys.length; i++) {
181
- const key = keys[i];
182
- const p = props[key];
183
- const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
184
- if (key === "children" && (state.isFunction(p) || state.isObservable(p))) {
185
- propsOut[key] = useSelector(p, { skipCheck: true });
186
- } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
187
- if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
188
- didWarnProps = true;
189
- console.warn(
190
- `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
191
- "$",
192
- ""
193
- )}. See https://legendapp.com/open-source/state/migrating for more details.`
194
- );
195
- }
196
- const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
197
- const bind = bindKeys == null ? void 0 : bindKeys[k];
198
- const shouldBind = bind && state.isObservable(p);
199
- propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : useSelector(p);
200
- if (shouldBind) {
201
- if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
202
- propsOut[k] = bind.defaultValue;
203
- }
204
- if (bind.handler && bind.getValue) {
205
- const handlerFn = (e) => {
206
- var _a;
207
- p.set(bind.getValue(e));
208
- (_a = props[bind.handler]) == null ? void 0 : _a.call(props, e);
209
- };
210
- propsOut[bind.handler] = // If in development mode, don't memoize the handler. fix fast refresh bug
211
- process.env.NODE_ENV === "development" ? handlerFn : React.useCallback(handlerFn, [props[bind.handler], bindKeys]);
204
+ const bindHandlersRef = React.useRef({});
205
+ argArray[0] = useSelector(
206
+ () => {
207
+ const propsOut = {};
208
+ const keys = Object.keys(props);
209
+ for (let i = 0; i < keys.length; i++) {
210
+ const key = keys[i];
211
+ const p = props[key];
212
+ const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
213
+ if (key === "children" && (state.isFunction(p) || state.isObservable(p))) {
214
+ propsOut[key] = state.computeSelector(p);
215
+ } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
216
+ if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
217
+ didWarnProps = true;
218
+ console.warn(
219
+ `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
220
+ "$",
221
+ ""
222
+ )}. See https://legendapp.com/open-source/state/migrating for more details.`
223
+ );
224
+ }
225
+ const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
226
+ const bind = bindKeys == null ? void 0 : bindKeys[k];
227
+ const shouldBind = bind && state.isObservable(p);
228
+ propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : state.computeSelector(p);
229
+ if (shouldBind) {
230
+ if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
231
+ propsOut[k] = bind.defaultValue;
232
+ }
233
+ if (bind.handler && bind.getValue) {
234
+ const handlerKey = bind.handler;
235
+ const originalHandler = props[handlerKey];
236
+ let cached = bindHandlersRef.current[k];
237
+ if (!cached || cached.bind !== bind || cached.observable !== p || cached.originalHandler !== originalHandler) {
238
+ cached = bindHandlersRef.current[k] = {
239
+ bind,
240
+ observable: p,
241
+ originalHandler,
242
+ handler: (event) => {
243
+ p.set(bind.getValue(event));
244
+ originalHandler == null ? void 0 : originalHandler(event);
245
+ }
246
+ };
247
+ }
248
+ propsOut[handlerKey] = cached.handler;
249
+ }
250
+ }
251
+ if (!isReactiveKey) {
252
+ delete propsOut[key];
253
+ }
254
+ } else if (propsOut[key] === void 0) {
255
+ propsOut[key] = p;
212
256
  }
213
257
  }
214
- if (!isReactiveKey) {
215
- delete propsOut[key];
216
- }
217
- } else if (propsOut[key] === void 0) {
218
- propsOut[key] = p;
219
- }
220
- }
221
- argArray[0] = propsOut;
258
+ return propsOut;
259
+ },
260
+ { skipCheck: true }
261
+ );
222
262
  }
223
263
  if (observe3) {
224
264
  return useSelector(
@@ -477,7 +517,7 @@ function useObservable(initialValue, deps) {
477
517
  ref.current.obs$ = state.observable(value);
478
518
  }
479
519
  if (depsObs$) {
480
- depsObs$.set(deps);
520
+ runInRender(() => depsObs$.set(deps));
481
521
  }
482
522
  useUnmount(() => {
483
523
  const obs = ref.current.obs$;
@@ -546,21 +586,23 @@ function useObserve(selector, reactionOrOptionsOrDeps, options, deps) {
546
586
  ref.current.selector = selector;
547
587
  ref.current.reaction = reaction;
548
588
  if (depsObs$) {
549
- depsObs$.set(deps);
589
+ runInRender(() => depsObs$.set(deps));
550
590
  }
551
591
  if (!ref.current.dispose) {
552
- ref.current.dispose = state.observe(
553
- (e) => {
554
- var _a;
555
- depsObs$ == null ? void 0 : depsObs$.get();
556
- const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
557
- return state.computeSelector(selector2, void 0, e);
558
- },
559
- (e) => {
560
- var _a, _b;
561
- return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
562
- },
563
- options
592
+ ref.current.dispose = runInRender(
593
+ () => state.observe(
594
+ (e) => {
595
+ var _a;
596
+ depsObs$ == null ? void 0 : depsObs$.get();
597
+ const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
598
+ return state.computeSelector(selector2, void 0, e);
599
+ },
600
+ (e) => {
601
+ var _a, _b;
602
+ return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
603
+ },
604
+ options
605
+ )
564
606
  );
565
607
  }
566
608
  useUnmountOnce(() => {
@@ -589,7 +631,7 @@ function useObserveEffect(selector, reactionOrOptionsOrDeps, options, deps) {
589
631
  ref.current.selector = selector;
590
632
  ref.current.reaction = reaction;
591
633
  if (depsObs$) {
592
- depsObs$.set(deps);
634
+ runInRender(() => depsObs$.set(deps));
593
635
  }
594
636
  useMountOnce(
595
637
  () => state.observe(
package/react.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { isFunction, isEmpty, observable, computeSelector, isArray, isMap, isObservableValueReady, isPromise, getNode as getNode$1, linked, observe, when, whenReady, internal, trackSelector, isPrimitive, isObservable } from '@legendapp/state';
2
- import React, { memo, forwardRef, createElement, useState, useContext, useMemo, useRef, useEffect, createContext, useCallback } from 'react';
2
+ import React, { memo, forwardRef, createElement, useState, useContext, useMemo, useRef, useEffect, createContext } from 'react';
3
3
  import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js';
4
4
  import { enableReactive } from '@legendapp/state/react-reactive/enableReactive';
5
5
 
@@ -7,8 +7,17 @@ import { enableReactive } from '@legendapp/state/react-reactive/enableReactive';
7
7
 
8
8
  // src/react/react-globals.ts
9
9
  var reactGlobals = {
10
- inObserver: false
10
+ inObserver: false,
11
+ renderDepth: 0
11
12
  };
13
+ function runInRender(fn) {
14
+ reactGlobals.renderDepth++;
15
+ try {
16
+ return fn();
17
+ } finally {
18
+ reactGlobals.renderDepth--;
19
+ }
20
+ }
12
21
  var pauseContext = void 0;
13
22
  var getPauseContext = () => {
14
23
  return pauseContext || (pauseContext = createContext(null));
@@ -30,6 +39,22 @@ function createSelectorFunctions(options, isPaused$) {
30
39
  let _selector;
31
40
  let prev;
32
41
  let pendingUpdate = void 0;
42
+ let notifyQueued = false;
43
+ const scheduleNotify = () => {
44
+ if (notify) {
45
+ if (reactGlobals.renderDepth > 0) {
46
+ if (!notifyQueued) {
47
+ notifyQueued = true;
48
+ queueMicrotask(() => {
49
+ notifyQueued = false;
50
+ notify == null ? void 0 : notify();
51
+ });
52
+ }
53
+ } else {
54
+ notify();
55
+ }
56
+ }
57
+ };
33
58
  const run = () => {
34
59
  dispose == null ? void 0 : dispose();
35
60
  const {
@@ -73,7 +98,7 @@ function createSelectorFunctions(options, isPaused$) {
73
98
  }
74
99
  if (changed) {
75
100
  version++;
76
- notify == null ? void 0 : notify();
101
+ scheduleNotify();
77
102
  }
78
103
  }
79
104
  };
@@ -85,6 +110,7 @@ function createSelectorFunctions(options, isPaused$) {
85
110
  }
86
111
  return () => {
87
112
  dispose == null ? void 0 : dispose();
113
+ notify = void 0;
88
114
  dispose = void 0;
89
115
  };
90
116
  },
@@ -117,7 +143,7 @@ function useSelector(selector, options) {
117
143
  const isPaused$ = useContext(getPauseContext());
118
144
  const selectorFn = useMemo(() => createSelectorFunctions(options, isPaused$), []);
119
145
  const { subscribe, getVersion, run } = selectorFn;
120
- value = run(selector);
146
+ value = runInRender(() => run(selector));
121
147
  useSyncExternalStore(subscribe, getVersion, getVersion);
122
148
  } catch (err) {
123
149
  if ((process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") && ((_a = err == null ? void 0 : err.message) == null ? void 0 : _a.includes("Rendered more"))) {
@@ -169,50 +195,64 @@ function createReactiveComponent(component, observe3, reactive2, keysReactive, b
169
195
  apply(target, thisArg, argArray) {
170
196
  if (reactive2) {
171
197
  const props = argArray[0];
172
- const propsOut = {};
173
- const keys = Object.keys(props);
174
- for (let i = 0; i < keys.length; i++) {
175
- const key = keys[i];
176
- const p = props[key];
177
- const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
178
- if (key === "children" && (isFunction(p) || isObservable(p))) {
179
- propsOut[key] = useSelector(p, { skipCheck: true });
180
- } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
181
- if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
182
- didWarnProps = true;
183
- console.warn(
184
- `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
185
- "$",
186
- ""
187
- )}. See https://legendapp.com/open-source/state/migrating for more details.`
188
- );
189
- }
190
- const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
191
- const bind = bindKeys == null ? void 0 : bindKeys[k];
192
- const shouldBind = bind && isObservable(p);
193
- propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : useSelector(p);
194
- if (shouldBind) {
195
- if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
196
- propsOut[k] = bind.defaultValue;
197
- }
198
- if (bind.handler && bind.getValue) {
199
- const handlerFn = (e) => {
200
- var _a;
201
- p.set(bind.getValue(e));
202
- (_a = props[bind.handler]) == null ? void 0 : _a.call(props, e);
203
- };
204
- propsOut[bind.handler] = // If in development mode, don't memoize the handler. fix fast refresh bug
205
- process.env.NODE_ENV === "development" ? handlerFn : useCallback(handlerFn, [props[bind.handler], bindKeys]);
198
+ const bindHandlersRef = useRef({});
199
+ argArray[0] = useSelector(
200
+ () => {
201
+ const propsOut = {};
202
+ const keys = Object.keys(props);
203
+ for (let i = 0; i < keys.length; i++) {
204
+ const key = keys[i];
205
+ const p = props[key];
206
+ const isReactiveKey = keysReactiveSet && keysReactiveSet.has(key);
207
+ if (key === "children" && (isFunction(p) || isObservable(p))) {
208
+ propsOut[key] = computeSelector(p);
209
+ } else if (isReactiveKey || key.startsWith("$") || key.endsWith("$")) {
210
+ if (process.env.NODE_ENV === "development" && !didWarnProps && key.endsWith("$")) {
211
+ didWarnProps = true;
212
+ console.warn(
213
+ `[legend-state] Reactive props were changed to start with $ instead of end with $ in version 2.0. So please change ${key} to $${key.replace(
214
+ "$",
215
+ ""
216
+ )}. See https://legendapp.com/open-source/state/migrating for more details.`
217
+ );
218
+ }
219
+ const k = isReactiveKey ? key : key.endsWith("$") ? key.slice(0, -1) : key.slice(1);
220
+ const bind = bindKeys == null ? void 0 : bindKeys[k];
221
+ const shouldBind = bind && isObservable(p);
222
+ propsOut[k] = shouldBind && (bind == null ? void 0 : bind.selector) ? bind.selector(propsOut, p) : computeSelector(p);
223
+ if (shouldBind) {
224
+ if (bind.defaultValue !== void 0 && propsOut[k] === void 0) {
225
+ propsOut[k] = bind.defaultValue;
226
+ }
227
+ if (bind.handler && bind.getValue) {
228
+ const handlerKey = bind.handler;
229
+ const originalHandler = props[handlerKey];
230
+ let cached = bindHandlersRef.current[k];
231
+ if (!cached || cached.bind !== bind || cached.observable !== p || cached.originalHandler !== originalHandler) {
232
+ cached = bindHandlersRef.current[k] = {
233
+ bind,
234
+ observable: p,
235
+ originalHandler,
236
+ handler: (event) => {
237
+ p.set(bind.getValue(event));
238
+ originalHandler == null ? void 0 : originalHandler(event);
239
+ }
240
+ };
241
+ }
242
+ propsOut[handlerKey] = cached.handler;
243
+ }
244
+ }
245
+ if (!isReactiveKey) {
246
+ delete propsOut[key];
247
+ }
248
+ } else if (propsOut[key] === void 0) {
249
+ propsOut[key] = p;
206
250
  }
207
251
  }
208
- if (!isReactiveKey) {
209
- delete propsOut[key];
210
- }
211
- } else if (propsOut[key] === void 0) {
212
- propsOut[key] = p;
213
- }
214
- }
215
- argArray[0] = propsOut;
252
+ return propsOut;
253
+ },
254
+ { skipCheck: true }
255
+ );
216
256
  }
217
257
  if (observe3) {
218
258
  return useSelector(
@@ -471,7 +511,7 @@ function useObservable(initialValue, deps) {
471
511
  ref.current.obs$ = observable(value);
472
512
  }
473
513
  if (depsObs$) {
474
- depsObs$.set(deps);
514
+ runInRender(() => depsObs$.set(deps));
475
515
  }
476
516
  useUnmount(() => {
477
517
  const obs = ref.current.obs$;
@@ -540,21 +580,23 @@ function useObserve(selector, reactionOrOptionsOrDeps, options, deps) {
540
580
  ref.current.selector = selector;
541
581
  ref.current.reaction = reaction;
542
582
  if (depsObs$) {
543
- depsObs$.set(deps);
583
+ runInRender(() => depsObs$.set(deps));
544
584
  }
545
585
  if (!ref.current.dispose) {
546
- ref.current.dispose = observe(
547
- (e) => {
548
- var _a;
549
- depsObs$ == null ? void 0 : depsObs$.get();
550
- const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
551
- return computeSelector(selector2, void 0, e);
552
- },
553
- (e) => {
554
- var _a, _b;
555
- return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
556
- },
557
- options
586
+ ref.current.dispose = runInRender(
587
+ () => observe(
588
+ (e) => {
589
+ var _a;
590
+ depsObs$ == null ? void 0 : depsObs$.get();
591
+ const selector2 = (_a = ref.current) == null ? void 0 : _a.selector;
592
+ return computeSelector(selector2, void 0, e);
593
+ },
594
+ (e) => {
595
+ var _a, _b;
596
+ return (_b = (_a = ref.current).reaction) == null ? void 0 : _b.call(_a, e);
597
+ },
598
+ options
599
+ )
558
600
  );
559
601
  }
560
602
  useUnmountOnce(() => {
@@ -583,7 +625,7 @@ function useObserveEffect(selector, reactionOrOptionsOrDeps, options, deps) {
583
625
  ref.current.selector = selector;
584
626
  ref.current.reaction = reaction;
585
627
  if (depsObs$) {
586
- depsObs$.set(deps);
628
+ runInRender(() => depsObs$.set(deps));
587
629
  }
588
630
  useMountOnce(
589
631
  () => observe(