@reckona/mreact-compat 0.0.144 → 0.0.146

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/dist/hooks.js CHANGED
@@ -2,6 +2,7 @@ import { flushPendingComputed, flushQueuedComputations, } from "@reckona/mreact-
2
2
  import { scheduleCallback } from "./fiber-scheduler.js";
3
3
  import { removeChildIfPresent } from "./dom-children.js";
4
4
  import { isReactCompatContext, readContextValue, useContext, withContextReadObserver, } from "./context.js";
5
+ import { REACTIVE_TEXT_BINDING_META } from "./element.js";
5
6
  import { isThenable } from "./thenable.js";
6
7
  const HOOK_RENDER_STATE_KEY = Symbol.for("modular.react.hook_render_state");
7
8
  const hookRenderState = (globalThis[HOOK_RENDER_STATE_KEY] ??= {
@@ -30,6 +31,8 @@ const strictMemoPrimitiveOwnerIds = new Map();
30
31
  const queuedTransitionRerenders = new Map();
31
32
  const queuedEventRerenders = new Set();
32
33
  export const version = "19.2.6";
34
+ const reactiveTextBindingsByNode = new WeakMap();
35
+ const hydratedIdsByRuntime = new WeakMap();
33
36
  export function act(callback) {
34
37
  const previousPriority = currentEventPriority;
35
38
  currentEventPriority = "discrete";
@@ -477,6 +480,17 @@ export function useState(initial) {
477
480
  slot.hostCommitValue = previousValue;
478
481
  }
479
482
  slot.value = nextValue;
483
+ const canUseDirectTextBinding = hookRenderState.hostCommitDepth === 0 &&
484
+ hookRenderState.currentRuntime !== runtime &&
485
+ hookRenderState.currentInstance !== instance &&
486
+ runtime.effectFlushPhase === undefined &&
487
+ eventBatchDepth === 0 &&
488
+ transitionDepth === 0 &&
489
+ optionsAllowDirectTextBinding(value) &&
490
+ updateDirectTextBinding(slot.textBinding, nextValue);
491
+ if (canUseDirectTextBinding) {
492
+ return;
493
+ }
480
494
  if (hookRenderState.hostCommitDepth > 0) {
481
495
  updateHostCommitDirtyState(instance);
482
496
  hookRenderState.queuedHostCommitRerenders.add(runtime);
@@ -488,7 +502,66 @@ export function useState(initial) {
488
502
  kind: "state",
489
503
  value: slot.value,
490
504
  });
491
- return [slot.value, setState];
505
+ const result = [slot.value, setState];
506
+ result[REACTIVE_TEXT_BINDING_META] = getStateTextBinding(slot);
507
+ return result;
508
+ }
509
+ export function subscribeReactiveTextBinding(binding, node) {
510
+ if (!isReactiveTextBinding(binding)) {
511
+ clearReactiveTextBinding(node);
512
+ return;
513
+ }
514
+ const previous = reactiveTextBindingsByNode.get(node);
515
+ if (previous !== undefined && previous !== binding) {
516
+ previous.subscribers.delete(node);
517
+ }
518
+ reactiveTextBindingsByNode.set(node, binding);
519
+ binding.subscribers.add(node);
520
+ }
521
+ function clearReactiveTextBinding(node) {
522
+ const previous = reactiveTextBindingsByNode.get(node);
523
+ if (previous === undefined) {
524
+ return;
525
+ }
526
+ previous.subscribers.delete(node);
527
+ reactiveTextBindingsByNode.delete(node);
528
+ }
529
+ function getStateTextBinding(slot) {
530
+ slot.textBinding ??= {
531
+ value: slot.value,
532
+ subscribers: new Set(),
533
+ };
534
+ slot.textBinding.value = slot.value;
535
+ return slot.textBinding;
536
+ }
537
+ function optionsAllowDirectTextBinding(value) {
538
+ return typeof value !== "function";
539
+ }
540
+ function updateDirectTextBinding(binding, value) {
541
+ if (binding === undefined || binding.subscribers.size === 0) {
542
+ return false;
543
+ }
544
+ let updated = false;
545
+ const nextText = String(value);
546
+ for (const node of binding.subscribers) {
547
+ if (node.parentNode === null) {
548
+ binding.subscribers.delete(node);
549
+ reactiveTextBindingsByNode.delete(node);
550
+ continue;
551
+ }
552
+ if (node.data !== nextText) {
553
+ node.data = nextText;
554
+ }
555
+ updated = true;
556
+ }
557
+ binding.value = value;
558
+ return updated;
559
+ }
560
+ function isReactiveTextBinding(value) {
561
+ return (typeof value === "object" &&
562
+ value !== null &&
563
+ "subscribers" in value &&
564
+ value.subscribers instanceof Set);
492
565
  }
493
566
  export function useReducer(reducer, initialArg, init) {
494
567
  const [state, setState] = runWithoutDevToolsHookTracking(() => useState(() => init === undefined ? initialArg : init(initialArg)));
@@ -526,11 +599,26 @@ export function useRef(initial) {
526
599
  }
527
600
  export function useId() {
528
601
  const runtime = requireRuntime();
602
+ const instance = requireInstance();
603
+ const idSlotKey = `${instance.path}:${instance.hookIndex}`;
529
604
  const idRef = runWithoutDevToolsHookTracking(() => useRef(undefined));
530
605
  if (idRef.current === undefined) {
531
- const mode = runtime.idMode === "server" ? "R" : "r";
532
- idRef.current = `_${runtime.identifierPrefix}${mode}_${runtime.idCounter}_`;
533
- runtime.idCounter += 1;
606
+ const hydratedId = runtime.idMode === "client"
607
+ ? hydratedIdsByRuntime.get(runtime)?.get(idSlotKey)
608
+ : undefined;
609
+ if (hydratedId !== undefined) {
610
+ idRef.current = hydratedId;
611
+ }
612
+ else {
613
+ const mode = runtime.idMode === "server" ? "R" : "r";
614
+ idRef.current = `_${runtime.identifierPrefix}${mode}_${runtime.idCounter}_`;
615
+ runtime.idCounter += 1;
616
+ if (runtime.idMode === "server") {
617
+ const hydratedIds = hydratedIdsByRuntime.get(runtime) ?? new Map();
618
+ hydratedIds.set(idSlotKey, idRef.current);
619
+ hydratedIdsByRuntime.set(runtime, hydratedIds);
620
+ }
621
+ }
534
622
  }
535
623
  recordDevToolsHook("useId", {
536
624
  kind: "id",
@@ -719,24 +807,32 @@ export function useLayoutEffect(callback, deps) {
719
807
  ? { kind: "effect", effectKind: "layout" }
720
808
  : { kind: "effect", effectKind: "layout", deps });
721
809
  }
722
- export function useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot = getSnapshot) {
810
+ export function useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
723
811
  const runtime = requireRuntime();
724
812
  const instance = requireInstance();
725
813
  const index = instance.hookIndex;
726
814
  instance.hookIndex += 1;
727
815
  let slot = instance.hooks[index];
728
816
  if (slot === undefined) {
729
- slot = { kind: "store", value: getServerSnapshot() };
817
+ slot = {
818
+ kind: "store",
819
+ value: runtime.idMode === "server" && getServerSnapshot !== undefined
820
+ ? getServerSnapshot()
821
+ : getSnapshot(),
822
+ };
730
823
  instance.hooks[index] = slot;
731
824
  }
732
825
  if (slot.kind !== "store") {
733
826
  throw new Error("Hook order changed between renders.");
734
827
  }
735
- const currentSnapshot = getSnapshot();
828
+ const isHydrationMount = runtime.idMode === "server" && slot.hasMounted !== true && getServerSnapshot !== undefined;
829
+ const currentSnapshot = isHydrationMount ? slot.value : getSnapshot();
736
830
  if (!Object.is(slot.value, currentSnapshot)) {
737
831
  slot.value = currentSnapshot;
738
832
  }
739
- recordExternalStoreCheck(getSnapshot, currentSnapshot);
833
+ if (!isHydrationMount) {
834
+ recordExternalStoreCheck(getSnapshot, currentSnapshot);
835
+ }
740
836
  runWithoutDevToolsHookTracking(() => useEffect(() => {
741
837
  const checkForUpdates = () => {
742
838
  if (instance.disposed === true) {