cx 26.7.1 → 26.7.2
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/ui/Cx.d.ts +2 -5
- package/build/ui/Cx.d.ts.map +1 -1
- package/build/ui/Cx.js +36 -38
- package/build/ui/Restate.d.ts +0 -6
- package/build/ui/Restate.d.ts.map +1 -1
- package/build/ui/Restate.js +1 -2
- package/dist/manifest.js +795 -795
- package/dist/ui.js +50 -47
- package/package.json +1 -1
- package/src/ui/Cx.tsx +416 -427
- package/src/ui/Restate.tsx +217 -226
package/dist/ui.js
CHANGED
|
@@ -2094,30 +2094,6 @@ class LinkedListsNode {
|
|
|
2094
2094
|
}
|
|
2095
2095
|
}
|
|
2096
2096
|
|
|
2097
|
-
// Optional guard (opt in per-instance via the `limitSyncBursts` prop) against React's "Maximum update depth
|
|
2098
|
-
// exceeded" during very large synchronous render bursts. On the initial render of a large document the store
|
|
2099
|
-
// can be mutated thousands of times within a single React commit: every Instance.setState wraps store.notify()
|
|
2100
|
-
// in batchUpdates(), so isBatchingUpdates() stays true and a subscribed Cx takes its *synchronous* setState
|
|
2101
|
-
// branch on each notification. React counts those as nested render-phase updates and aborts past ~50
|
|
2102
|
-
// (surfacing as the recoverable "error during concurrent rendering"). When a Cx opts in, once back-to-back
|
|
2103
|
-
// updates exceed the limit it falls back to the coalesced setTimeout branch, which renders the latest store
|
|
2104
|
-
// data on the next tick. The counter is global (it mirrors React's global nested-update counter) and resets
|
|
2105
|
-
// after any idle gap. It is advanced on every update() of an opted-in Cx -- counting only the synchronous
|
|
2106
|
-
// branch would let the window reset whenever a deferred update lands between two synchronous ones, so the
|
|
2107
|
-
// counter would never reach the limit. performance.now() gives sub-ms resolution (Date.now() is the fallback;
|
|
2108
|
-
// Timing.now() can't be used here because it returns 0 in production).
|
|
2109
|
-
const SYNC_UPDATE_BURST_LIMIT = 30;
|
|
2110
|
-
const SYNC_UPDATE_BURST_RESET_MS = 10;
|
|
2111
|
-
const syncUpdateBurstNow =
|
|
2112
|
-
typeof performance !== "undefined" && performance.now ? () => performance.now() : () => Date.now();
|
|
2113
|
-
let syncUpdateBurstCount = 0;
|
|
2114
|
-
let syncUpdateBurstLastAt = 0;
|
|
2115
|
-
function trackSyncUpdateBurst() {
|
|
2116
|
-
let t = syncUpdateBurstNow();
|
|
2117
|
-
if (t - syncUpdateBurstLastAt > SYNC_UPDATE_BURST_RESET_MS) syncUpdateBurstCount = 0;
|
|
2118
|
-
syncUpdateBurstLastAt = t;
|
|
2119
|
-
return ++syncUpdateBurstCount > SYNC_UPDATE_BURST_LIMIT;
|
|
2120
|
-
}
|
|
2121
2097
|
class Cx extends VDOM.Component {
|
|
2122
2098
|
widget;
|
|
2123
2099
|
store;
|
|
@@ -2131,6 +2107,8 @@ class Cx extends VDOM.Component {
|
|
|
2131
2107
|
deferCounter;
|
|
2132
2108
|
pendingUpdateTimer;
|
|
2133
2109
|
unsubscribeIdleRequest;
|
|
2110
|
+
// 0 when no synchronous setState is in flight; >0 while one is pending (re-entrancy guard for update())
|
|
2111
|
+
stateUpdateDepth = 0;
|
|
2134
2112
|
constructor(props) {
|
|
2135
2113
|
super(props);
|
|
2136
2114
|
if (props.instance) {
|
|
@@ -2156,6 +2134,7 @@ class Cx extends VDOM.Component {
|
|
|
2156
2134
|
this.unsubscribe = this.store.subscribe(this.update.bind(this));
|
|
2157
2135
|
this.state.data = this.store.getData();
|
|
2158
2136
|
}
|
|
2137
|
+
this.onStateUpdateCompleted = this.onStateUpdateCompleted.bind(this);
|
|
2159
2138
|
this.flags = {};
|
|
2160
2139
|
this.renderCount = 0;
|
|
2161
2140
|
if (props.onError) this.componentDidCatch = this.componentDidCatchHandler.bind(this);
|
|
@@ -2187,7 +2166,7 @@ class Cx extends VDOM.Component {
|
|
|
2187
2166
|
return this.instance;
|
|
2188
2167
|
}
|
|
2189
2168
|
if (this.widget && this.parentInstance)
|
|
2190
|
-
return (this.instance = this.parentInstance.getDetachedChild(this.widget, 0, this.store));
|
|
2169
|
+
return (this.instance = this.parentInstance.getDetachedChild(this.widget, "0", this.store));
|
|
2191
2170
|
throw new Error("Could not resolve a widget instance in the Cx component.");
|
|
2192
2171
|
}
|
|
2193
2172
|
render() {
|
|
@@ -2216,30 +2195,33 @@ class Cx extends VDOM.Component {
|
|
|
2216
2195
|
let data = this.store.getData();
|
|
2217
2196
|
debug(appDataFlag, data);
|
|
2218
2197
|
if (this.flags.preparing) this.flags.dirty = true;
|
|
2219
|
-
//
|
|
2220
|
-
//
|
|
2221
|
-
//
|
|
2222
|
-
//
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2198
|
+
// Synchronous path (while batching, or for `immediate` instances). At most one setState may be in flight
|
|
2199
|
+
// per Cx: if one is already pending (stateUpdateDepth != 0) skip issuing another. Re-entrant setStates
|
|
2200
|
+
// nested deep -- e.g. a large initial render that writes to the store while it renders -- are what trip
|
|
2201
|
+
// React's "Maximum update depth exceeded"; coalescing them into a single in-flight update avoids that.
|
|
2202
|
+
// The pending update's completion callback re-reads the store and renders again if it changed
|
|
2203
|
+
// (see onStateUpdateCompleted), so no change is dropped.
|
|
2204
|
+
else if (this.props.immediate || isBatchingUpdates()) {
|
|
2205
|
+
if (this.stateUpdateDepth == 0) {
|
|
2206
|
+
this.stateUpdateDepth = 1;
|
|
2207
|
+
notifyBatchedUpdateStarting();
|
|
2208
|
+
this.setState(
|
|
2209
|
+
{
|
|
2210
|
+
data: data,
|
|
2211
|
+
},
|
|
2212
|
+
this.onStateUpdateCompleted,
|
|
2213
|
+
);
|
|
2214
|
+
}
|
|
2231
2215
|
} else {
|
|
2232
|
-
//
|
|
2233
|
-
//synchronous burst limit above, which falls through here to avoid React's max-update-depth abort
|
|
2216
|
+
// standard mode: coalesce sequential store commands into a single deferred update
|
|
2234
2217
|
if (!this.pendingUpdateTimer) {
|
|
2235
2218
|
notifyBatchedUpdateStarting();
|
|
2236
2219
|
this.pendingUpdateTimer = setTimeout(() => {
|
|
2237
2220
|
delete this.pendingUpdateTimer;
|
|
2238
|
-
//
|
|
2239
|
-
//left stale after a burst; otherwise keep the original captured-data behavior unchanged
|
|
2221
|
+
// read fresh data at fire time so the coalesced update renders the latest store state
|
|
2240
2222
|
this.setState(
|
|
2241
2223
|
{
|
|
2242
|
-
data: this.
|
|
2224
|
+
data: this.store.getData(),
|
|
2243
2225
|
},
|
|
2244
2226
|
notifyBatchedUpdateCompleted,
|
|
2245
2227
|
);
|
|
@@ -2247,15 +2229,38 @@ class Cx extends VDOM.Component {
|
|
|
2247
2229
|
}
|
|
2248
2230
|
}
|
|
2249
2231
|
}
|
|
2232
|
+
// Completion callback for the synchronous setState above. If the store changed while that update was
|
|
2233
|
+
// rendering -- i.e. the render itself wrote to the store, as happens throughout a large initial render --
|
|
2234
|
+
// render once more with the latest data, keeping this callback armed; otherwise the burst has settled, so
|
|
2235
|
+
// clear the in-flight flag and report completion. Re-arming coalesces until the store reaches a fixpoint;
|
|
2236
|
+
// a genuinely non-converging update loop is still caught by React's own max-update-depth guard.
|
|
2237
|
+
onStateUpdateCompleted() {
|
|
2238
|
+
let latestData = this.store.getData();
|
|
2239
|
+
if (this.state.data === latestData) {
|
|
2240
|
+
this.stateUpdateDepth = 0;
|
|
2241
|
+
notifyBatchedUpdateCompleted();
|
|
2242
|
+
} else {
|
|
2243
|
+
this.stateUpdateDepth++;
|
|
2244
|
+
this.setState(
|
|
2245
|
+
{
|
|
2246
|
+
data: latestData,
|
|
2247
|
+
},
|
|
2248
|
+
this.onStateUpdateCompleted,
|
|
2249
|
+
);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2250
2252
|
waitForIdle() {
|
|
2251
2253
|
if (!this.props.deferredUntilIdle) return;
|
|
2252
2254
|
if (this.unsubscribeIdleRequest) this.unsubscribeIdleRequest();
|
|
2253
2255
|
let token = ++this.deferCounter;
|
|
2254
2256
|
this.unsubscribeIdleRequest = onIdleCallback(
|
|
2255
2257
|
() => {
|
|
2256
|
-
this.setState(
|
|
2257
|
-
|
|
2258
|
-
|
|
2258
|
+
this.setState(
|
|
2259
|
+
{
|
|
2260
|
+
deferToken: token,
|
|
2261
|
+
},
|
|
2262
|
+
this.onStateUpdateCompleted,
|
|
2263
|
+
);
|
|
2259
2264
|
},
|
|
2260
2265
|
{
|
|
2261
2266
|
timeout: this.props.idleTimeout || 30000,
|
|
@@ -2693,7 +2698,6 @@ class Restate extends PureContainerBase {
|
|
|
2693
2698
|
deferredUntilIdle: instance.data.deferredUntilIdle,
|
|
2694
2699
|
idleTimeout: instance.data.idleTimeout,
|
|
2695
2700
|
immediate: this.immediate,
|
|
2696
|
-
limitSyncBursts: this.limitSyncBursts,
|
|
2697
2701
|
cultureInfo: instance.cultureInfo,
|
|
2698
2702
|
},
|
|
2699
2703
|
key,
|
|
@@ -2703,7 +2707,6 @@ class Restate extends PureContainerBase {
|
|
|
2703
2707
|
Restate.prototype.detached = false;
|
|
2704
2708
|
Restate.prototype.waitForIdle = false;
|
|
2705
2709
|
Restate.prototype.immediate = false;
|
|
2706
|
-
Restate.prototype.limitSyncBursts = false;
|
|
2707
2710
|
Restate.prototype.culture = null;
|
|
2708
2711
|
const PrivateStore = Restate;
|
|
2709
2712
|
class RestateStore extends Store {
|