@reckona/mreact-compat 0.0.164 → 0.0.165

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/src/hooks.ts CHANGED
@@ -143,7 +143,7 @@ type HookSlot =
143
143
  update: (state: unknown, payload: unknown) => unknown;
144
144
  dispatch?: (payload: unknown) => void;
145
145
  }
146
- | { kind: "store"; value: unknown; hasMounted?: boolean }
146
+ | { kind: "store"; value: unknown; hasMounted?: boolean; hostCommitValue?: unknown }
147
147
  | { kind: "ref"; value: { current: unknown } }
148
148
  | { kind: "memo"; value: unknown; deps?: readonly unknown[] }
149
149
  | { kind: "debug"; value: unknown }
@@ -191,6 +191,7 @@ let currentEventPriority: EventPriority = "default";
191
191
  let eventRerenderScheduled = false;
192
192
  let automaticRerenderScheduled = false;
193
193
  let effectFlushRerenderDepth = 0;
194
+ let hostCommitRerenderDepth = 0;
194
195
  let strictMemoOwnerId = 0;
195
196
  const strictMemoObjectOwnerIds = new WeakMap<object, number>();
196
197
  const queuedTransitionRerenders = new Map<RootRuntime, TransitionContext>();
@@ -366,8 +367,6 @@ export function createRootRuntime(
366
367
  hookRenderState.currentInstance = undefined;
367
368
  if (committed) {
368
369
  flushProfilerCommits(this, profilerCommits);
369
- flushHostCommitRerenders();
370
- this.externalStoreUpdate = false;
371
370
  }
372
371
  },
373
372
  flushEffects() {
@@ -389,7 +388,9 @@ export function createRootRuntime(
389
388
  this.effectFlushPhase = undefined;
390
389
  this.profilerFlushDepth -= 1;
391
390
  if (this.profilerFlushDepth === 0) {
391
+ flushHostCommitRerenders();
392
392
  flushEffectFlushRerenders();
393
+ this.externalStoreUpdate = false;
393
394
  }
394
395
  }
395
396
  },
@@ -865,6 +866,15 @@ function clearReactiveTextBinding(node: Text): void {
865
866
  reactiveTextBindingsByNode.delete(node);
866
867
  }
867
868
 
869
+ function clearReactiveTextBindingSubscribers(binding: ReactiveTextBinding): void {
870
+ for (const node of binding.subscribers) {
871
+ if (reactiveTextBindingsByNode.get(node) === binding) {
872
+ reactiveTextBindingsByNode.delete(node);
873
+ }
874
+ }
875
+ binding.subscribers.clear();
876
+ }
877
+
868
878
  function getStateTextBinding(slot: Extract<HookSlot, { kind: "state" }>): ReactiveTextBinding {
869
879
  slot.textBinding ??= {
870
880
  value: slot.value,
@@ -1312,13 +1322,25 @@ export function useSyncExternalStore<T>(
1312
1322
  const nextSnapshot = getSnapshot();
1313
1323
 
1314
1324
  if (!Object.is(slot.value, nextSnapshot)) {
1325
+ if (hookRenderState.hostCommitDepth > 0 && !Object.hasOwn(slot, "hostCommitValue")) {
1326
+ slot.hostCommitValue = slot.value;
1327
+ }
1315
1328
  slot.value = nextSnapshot;
1316
- instance.dirty = true;
1317
1329
  runtime.externalStoreUpdate = true;
1330
+ if (hookRenderState.hostCommitDepth > 0) {
1331
+ updateHostCommitDirtyState(instance);
1332
+ hookRenderState.queuedHostCommitRerenders.add(runtime);
1333
+ return;
1334
+ }
1335
+ instance.dirty = true;
1318
1336
  if (runtime.profilerFlushDepth > 0) {
1319
1337
  hookRenderState.queuedEffectFlushRerenders.add(runtime);
1320
1338
  return;
1321
1339
  }
1340
+ if (eventBatchDepth > 0) {
1341
+ queueEventRerender(runtime);
1342
+ return;
1343
+ }
1322
1344
  runtime.rerender("sync");
1323
1345
  }
1324
1346
  };
@@ -1567,6 +1589,7 @@ export function startTransition(scope: TransitionScope): void {
1567
1589
  export function runWithEventPriority<T>(
1568
1590
  priority: EventPriority,
1569
1591
  callback: () => T,
1592
+ deferFlush?: (flush: () => void) => void,
1570
1593
  ): T {
1571
1594
  const previousPriority = currentEventPriority;
1572
1595
  currentEventPriority = priority;
@@ -1579,7 +1602,14 @@ export function runWithEventPriority<T>(
1579
1602
  currentEventPriority = previousPriority;
1580
1603
 
1581
1604
  if (eventBatchDepth === 0) {
1582
- flushEventRerendersForPriority(priority);
1605
+ const flush = () => {
1606
+ flushEventRerendersForPriority(priority);
1607
+ };
1608
+ if (deferFlush === undefined) {
1609
+ flush();
1610
+ } else {
1611
+ deferFlush(flush);
1612
+ }
1583
1613
  }
1584
1614
  }
1585
1615
  }
@@ -2016,23 +2046,36 @@ function scheduleInstanceUpdate(
2016
2046
 
2017
2047
  function flushHostCommitRerenders(): void {
2018
2048
  if (
2049
+ hostCommitRerenderDepth > 0 ||
2019
2050
  hookRenderState.hostCommitDepth > 0 ||
2020
2051
  hookRenderState.queuedHostCommitRerenders.size === 0
2021
2052
  ) {
2022
2053
  return;
2023
2054
  }
2024
2055
 
2025
- const runtimes = [...hookRenderState.queuedHostCommitRerenders];
2026
- hookRenderState.queuedHostCommitRerenders.clear();
2027
- for (const runtime of runtimes) {
2028
- const hasDirtyInstance = Array.from(runtime.instances.values()).some(
2029
- (instance) => instance.dirty,
2030
- );
2031
- clearHostCommitStateBaselines(runtime);
2056
+ hostCommitRerenderDepth += 1;
2057
+ try {
2058
+ for (
2059
+ let attempt = 0;
2060
+ attempt < 3 && hookRenderState.queuedHostCommitRerenders.size > 0;
2061
+ attempt += 1
2062
+ ) {
2063
+ const runtimes = [...hookRenderState.queuedHostCommitRerenders];
2064
+ hookRenderState.queuedHostCommitRerenders.clear();
2065
+ for (const runtime of runtimes) {
2066
+ const hasDirtyInstance = Array.from(runtime.instances.values()).some(
2067
+ (instance) => instance.dirty,
2068
+ );
2069
+ clearHostCommitStateBaselines(runtime);
2032
2070
 
2033
- if (hasDirtyInstance) {
2034
- runtime.rerender("sync");
2071
+ if (hasDirtyInstance) {
2072
+ runtime.rerender("sync");
2073
+ }
2074
+ }
2035
2075
  }
2076
+ hookRenderState.queuedHostCommitRerenders.clear();
2077
+ } finally {
2078
+ hostCommitRerenderDepth -= 1;
2036
2079
  }
2037
2080
  }
2038
2081
 
@@ -2074,7 +2117,7 @@ function updateHostCommitDirtyState(
2074
2117
  ): void {
2075
2118
  instance.dirty = instance.hooks.some(
2076
2119
  (slot) =>
2077
- slot.kind === "state" &&
2120
+ (slot.kind === "state" || slot.kind === "store") &&
2078
2121
  Object.hasOwn(slot, "hostCommitValue") &&
2079
2122
  !Object.is(slot.hostCommitValue, slot.value),
2080
2123
  );
@@ -2083,7 +2126,7 @@ function updateHostCommitDirtyState(
2083
2126
  function clearHostCommitStateBaselines(runtime: RootRuntime): void {
2084
2127
  for (const instance of runtime.instances.values()) {
2085
2128
  for (const slot of instance.hooks) {
2086
- if (slot.kind === "state") {
2129
+ if (slot.kind === "state" || slot.kind === "store") {
2087
2130
  delete slot.hostCommitValue;
2088
2131
  }
2089
2132
  }
@@ -2228,6 +2271,8 @@ function cleanupInstance(instance: ComponentInstance): void {
2228
2271
  slot.mounted = false;
2229
2272
  slot.cleanup?.();
2230
2273
  delete slot.cleanup;
2274
+ } else if (slot?.kind === "state" && slot.textBinding !== undefined) {
2275
+ clearReactiveTextBindingSubscribers(slot.textBinding);
2231
2276
  }
2232
2277
  }
2233
2278
  }