@reckona/mreact-compat 0.0.170 → 0.0.172
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/devtools.js +2 -0
- package/dist/devtools.js.map +1 -1
- package/dist/dom-host-rules.d.ts.map +1 -1
- package/dist/dom-host-rules.js +4 -1
- package/dist/dom-host-rules.js.map +1 -1
- package/dist/dom-props.js +2 -1
- package/dist/dom-props.js.map +1 -1
- package/dist/element.d.ts +13 -1
- package/dist/element.d.ts.map +1 -1
- package/dist/element.js +30 -2
- package/dist/element.js.map +1 -1
- package/dist/fiber-commit.js +2 -1
- package/dist/fiber-commit.js.map +1 -1
- package/dist/fiber-host.d.ts +1 -1
- package/dist/fiber-host.d.ts.map +1 -1
- package/dist/fiber-host.js +1 -1
- package/dist/fiber-host.js.map +1 -1
- package/dist/fiber.d.ts +2 -1
- package/dist/fiber.d.ts.map +1 -1
- package/dist/fiber.js +2 -0
- package/dist/fiber.js.map +1 -1
- package/dist/hooks.d.ts +7 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +122 -41
- package/dist/hooks.js.map +1 -1
- package/dist/host-reconciler.d.ts +1 -0
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +659 -50
- package/dist/host-reconciler.js.map +1 -1
- package/dist/jsx-dev-runtime.d.ts +3 -1
- package/dist/jsx-dev-runtime.d.ts.map +1 -1
- package/dist/jsx-dev-runtime.js +3 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.d.ts +4 -2
- package/dist/jsx-runtime.d.ts.map +1 -1
- package/dist/jsx-runtime.js +9 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/reactive-prop-cell.d.ts +13 -0
- package/dist/reactive-prop-cell.d.ts.map +1 -0
- package/dist/reactive-prop-cell.js +36 -0
- package/dist/reactive-prop-cell.js.map +1 -0
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +3 -1
- package/dist/root.js.map +1 -1
- package/package.json +3 -3
- package/src/devtools.ts +2 -0
- package/src/dom-host-rules.ts +4 -1
- package/src/dom-props.ts +2 -1
- package/src/element.ts +60 -3
- package/src/fiber-commit.ts +3 -1
- package/src/fiber-host.ts +1 -0
- package/src/fiber.ts +4 -0
- package/src/hooks.ts +173 -41
- package/src/host-reconciler.ts +979 -64
- package/src/jsx-dev-runtime.ts +4 -0
- package/src/jsx-runtime.ts +16 -0
- package/src/reactive-prop-cell.ts +67 -0
- package/src/root.ts +3 -0
package/src/host-reconciler.ts
CHANGED
|
@@ -7,16 +7,25 @@ import {
|
|
|
7
7
|
LAZY_TYPE,
|
|
8
8
|
MEMO_TYPE,
|
|
9
9
|
Profiler,
|
|
10
|
+
REACTIVE_DOM_BLOCK_TYPE,
|
|
10
11
|
REACTIVE_TEXT_BINDING_META,
|
|
11
12
|
STRICT_MODE_TYPE,
|
|
12
13
|
Suspense,
|
|
13
14
|
SuspenseList,
|
|
14
15
|
type ReactCompatElement,
|
|
15
16
|
type ReactCompatPortal,
|
|
17
|
+
type ReactiveDomBlockProps,
|
|
18
|
+
type ReactiveDomBlockResult,
|
|
16
19
|
isReactCompatElement,
|
|
17
20
|
isReactCompatPortal,
|
|
18
21
|
type ReactCompatNode,
|
|
19
22
|
} from "./element.js";
|
|
23
|
+
import {
|
|
24
|
+
createReactivePropCell,
|
|
25
|
+
createReactivePropProxy,
|
|
26
|
+
setReactivePropCell,
|
|
27
|
+
type ReactiveDomBlockState,
|
|
28
|
+
} from "./reactive-prop-cell.js";
|
|
20
29
|
import {
|
|
21
30
|
consumerContext,
|
|
22
31
|
isReactCompatConsumer,
|
|
@@ -36,6 +45,7 @@ import {
|
|
|
36
45
|
namespaceForHostChildren,
|
|
37
46
|
namespaceForHostElement,
|
|
38
47
|
type CustomHostDocument,
|
|
48
|
+
type HostElement,
|
|
39
49
|
type HostNamespace,
|
|
40
50
|
} from "./dom-host-rules.js";
|
|
41
51
|
import { createFiber, createWorkInProgress, type Fiber, type FiberRoot } from "./fiber.js";
|
|
@@ -96,6 +106,7 @@ interface SuspenseFiberState {
|
|
|
96
106
|
|
|
97
107
|
const committedPortalContainers = new Set<Element>();
|
|
98
108
|
const pendingHostRefUpdates: { ref: unknown; node: unknown }[] = [];
|
|
109
|
+
const emptyInstanceKeys: string[] = [];
|
|
99
110
|
|
|
100
111
|
interface FiberHydrationOptions extends RenderOptions {
|
|
101
112
|
previousNodes?: readonly Node[];
|
|
@@ -174,6 +185,10 @@ export function canRenderHostFiber(node: ReactCompatNode): boolean {
|
|
|
174
185
|
return true;
|
|
175
186
|
}
|
|
176
187
|
|
|
188
|
+
if (node.type === REACTIVE_DOM_BLOCK_TYPE) {
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
|
|
177
192
|
if (isReactCompatProvider(node.type)) {
|
|
178
193
|
return canRenderHostFiber(node.props.children as ReactCompatNode);
|
|
179
194
|
}
|
|
@@ -258,7 +273,28 @@ export function commitHostFiberRoot(
|
|
|
258
273
|
pendingHostRefUpdates.length = 0;
|
|
259
274
|
const commitPath = getRootCommitPath(options);
|
|
260
275
|
if (!hasChildListMutation(finishedWork)) {
|
|
261
|
-
|
|
276
|
+
commitHostDirtyChildrenOf(
|
|
277
|
+
finishedWork,
|
|
278
|
+
finishedWork.child,
|
|
279
|
+
root.container,
|
|
280
|
+
root.container,
|
|
281
|
+
commitPath,
|
|
282
|
+
options,
|
|
283
|
+
);
|
|
284
|
+
committed = true;
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (
|
|
289
|
+
finishedWork.childListChanged &&
|
|
290
|
+
commitHostKeyedChildListMutationFiber(
|
|
291
|
+
finishedWork,
|
|
292
|
+
root.container,
|
|
293
|
+
root.container,
|
|
294
|
+
commitPath,
|
|
295
|
+
options,
|
|
296
|
+
)
|
|
297
|
+
) {
|
|
262
298
|
committed = true;
|
|
263
299
|
return;
|
|
264
300
|
}
|
|
@@ -317,6 +353,42 @@ export function commitHydratingHostFiberRoot(
|
|
|
317
353
|
}
|
|
318
354
|
}
|
|
319
355
|
|
|
356
|
+
export function disposeHostFiberResources(fiber: Fiber | undefined): void {
|
|
357
|
+
if (fiber === undefined || fiber.hasDisposableResources !== true) {
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Dispose this fiber and its SUBTREE only — not its siblings. This is called
|
|
362
|
+
// once per deleted fiber, and deleted siblings are disposed by their own
|
|
363
|
+
// calls; walking siblings here re-walked the whole deleted list per deletion
|
|
364
|
+
// (O(n^2) on a cleared 1k-row reactive list). The dedupe set is allocated
|
|
365
|
+
// once for the subtree walk.
|
|
366
|
+
const seen = new Set<unknown>();
|
|
367
|
+
if (fiber.tag === "reactive-dom-block") {
|
|
368
|
+
disposeReactiveDomBlockState(fiber.stateNode, seen);
|
|
369
|
+
}
|
|
370
|
+
disposeHostFiberChildResources(fiber.child, seen);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function disposeHostFiberChildResources(
|
|
374
|
+
fiber: Fiber | undefined,
|
|
375
|
+
seen: Set<unknown>,
|
|
376
|
+
): void {
|
|
377
|
+
let cursor = fiber;
|
|
378
|
+
|
|
379
|
+
while (cursor !== undefined) {
|
|
380
|
+
if (cursor.hasDisposableResources === true) {
|
|
381
|
+
if (cursor.tag === "reactive-dom-block") {
|
|
382
|
+
disposeReactiveDomBlockState(cursor.stateNode, seen);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
disposeHostFiberChildResources(cursor.child, seen);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
cursor = cursor.sibling;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
320
392
|
function reconcileHostChild(
|
|
321
393
|
parent: Fiber,
|
|
322
394
|
currentFirstChild: Fiber | undefined,
|
|
@@ -342,7 +414,15 @@ function reconcileHostChild(
|
|
|
342
414
|
const rowResult =
|
|
343
415
|
children === undefined
|
|
344
416
|
? undefined
|
|
345
|
-
: reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options)
|
|
417
|
+
: reconcileKeyedRowHostChildren(parent, currentFirstChild, children, options) ??
|
|
418
|
+
reconcileKeyedMemoRowHostChildren(
|
|
419
|
+
parent,
|
|
420
|
+
currentFirstChild,
|
|
421
|
+
children,
|
|
422
|
+
runtime,
|
|
423
|
+
path,
|
|
424
|
+
options,
|
|
425
|
+
);
|
|
346
426
|
if (rowResult !== undefined) {
|
|
347
427
|
return rowResult;
|
|
348
428
|
}
|
|
@@ -359,6 +439,7 @@ function reconcileHostChild(
|
|
|
359
439
|
let skipRemainingKeyedLookup = false;
|
|
360
440
|
let sequentialCurrentCursor = currentFirstChild;
|
|
361
441
|
let childListOrderChanged = false;
|
|
442
|
+
let dirtyChildren: Fiber[] | undefined;
|
|
362
443
|
let usedCurrentChildren =
|
|
363
444
|
currentFirstChild === undefined || hasKeyedChildren ? undefined : new Set<Fiber>();
|
|
364
445
|
const ensureUsedCurrentChildren = (): Set<Fiber> => {
|
|
@@ -404,11 +485,28 @@ function reconcileHostChild(
|
|
|
404
485
|
currentKeyed?.sibling?.key === key &&
|
|
405
486
|
canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)
|
|
406
487
|
) {
|
|
488
|
+
const deleted = currentKeyed;
|
|
489
|
+
const matched = currentKeyed.sibling;
|
|
490
|
+
const suffixResult = tryReuseDependencyFreeMemoRemovalSuffix(
|
|
491
|
+
parent,
|
|
492
|
+
children,
|
|
493
|
+
index,
|
|
494
|
+
deleted,
|
|
495
|
+
matched,
|
|
496
|
+
runtime,
|
|
497
|
+
options,
|
|
498
|
+
first,
|
|
499
|
+
previous,
|
|
500
|
+
consumed,
|
|
501
|
+
);
|
|
502
|
+
if (suffixResult !== undefined) {
|
|
503
|
+
return suffixResult;
|
|
504
|
+
}
|
|
407
505
|
childListOrderChanged = true;
|
|
408
506
|
ensureUsedCurrentChildren();
|
|
409
|
-
matchedCurrent =
|
|
507
|
+
matchedCurrent = matched;
|
|
410
508
|
canReuseMatchedCurrentFiber = false;
|
|
411
|
-
currentKeyed =
|
|
509
|
+
currentKeyed = matched.sibling;
|
|
412
510
|
} else {
|
|
413
511
|
if (
|
|
414
512
|
children !== undefined &&
|
|
@@ -489,6 +587,14 @@ function reconcileHostChild(
|
|
|
489
587
|
&& fiber.memoizedState === undefined
|
|
490
588
|
) {
|
|
491
589
|
fiber.memoizedState = index;
|
|
590
|
+
} else if (
|
|
591
|
+
fiber.tag === "memo" &&
|
|
592
|
+
(fiber.stateNode === undefined || typeof fiber.stateNode === "number")
|
|
593
|
+
) {
|
|
594
|
+
fiber.stateNode = index;
|
|
595
|
+
}
|
|
596
|
+
if (hasHostCommitWork(fiber)) {
|
|
597
|
+
(dirtyChildren ??= []).push(fiber);
|
|
492
598
|
}
|
|
493
599
|
previous = fiber;
|
|
494
600
|
}
|
|
@@ -500,6 +606,7 @@ function reconcileHostChild(
|
|
|
500
606
|
}
|
|
501
607
|
parent.childListChanged =
|
|
502
608
|
childListOrderChanged || childFiberListShapeChanged(currentFirstChild, first);
|
|
609
|
+
recordDirtyChildCommitHints(parent, dirtyChildren);
|
|
503
610
|
|
|
504
611
|
return { fiber: first, consumed };
|
|
505
612
|
}
|
|
@@ -528,6 +635,7 @@ function reconcileKeyedRowHostChildren(
|
|
|
528
635
|
let subtreeFlags = NoFlags;
|
|
529
636
|
let subtreeChildListChanged = false;
|
|
530
637
|
let hasRefSubtree = false;
|
|
638
|
+
let hasDisposableResources = false;
|
|
531
639
|
let appendSuffix: AppendSuffixCommitHint | undefined;
|
|
532
640
|
const canReuseUnchangedRows = hasSameKeyOrderPrefix(currentFirstChild, children);
|
|
533
641
|
const row = createKeyedRowHostElementScratch();
|
|
@@ -593,6 +701,9 @@ function reconcileKeyedRowHostChildren(
|
|
|
593
701
|
if (fiber.hasRefSubtree) {
|
|
594
702
|
hasRefSubtree = true;
|
|
595
703
|
}
|
|
704
|
+
if (fiber.hasDisposableResources) {
|
|
705
|
+
hasDisposableResources = true;
|
|
706
|
+
}
|
|
596
707
|
subtreeFlags |= fiber.flags | fiber.subtreeFlags;
|
|
597
708
|
subtreeChildListChanged =
|
|
598
709
|
subtreeChildListChanged ||
|
|
@@ -610,6 +721,7 @@ function reconcileKeyedRowHostChildren(
|
|
|
610
721
|
}
|
|
611
722
|
|
|
612
723
|
parent.hasRefSubtree = hasRefSubtree;
|
|
724
|
+
parent.hasDisposableResources = hasDisposableResources;
|
|
613
725
|
parent.subtreeFlags = subtreeFlags;
|
|
614
726
|
parent.subtreeChildListChanged = subtreeChildListChanged;
|
|
615
727
|
parent.childListChanged = listShapeChanged;
|
|
@@ -619,6 +731,382 @@ function reconcileKeyedRowHostChildren(
|
|
|
619
731
|
return { fiber: first, consumed: 0 };
|
|
620
732
|
}
|
|
621
733
|
|
|
734
|
+
function isKeyedMemoRowCandidate(node: ReactCompatNode): boolean {
|
|
735
|
+
return (
|
|
736
|
+
isReactCompatElement(node) &&
|
|
737
|
+
node.key !== null &&
|
|
738
|
+
node.ref === null &&
|
|
739
|
+
isMemoType(node.type)
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Marker the compiler stamps on a lowered host-only component that returns its
|
|
744
|
+
// props verbatim as a reactive block (`createReactiveDomBlock(render, props)`).
|
|
745
|
+
// Such a component is pure and structurally static, so on a props change the
|
|
746
|
+
// reconciler can drive its committed block straight through the prop cell instead
|
|
747
|
+
// of re-invoking the component (the block's bound DOM updates via subscriptions).
|
|
748
|
+
const STATIC_REACTIVE_BLOCK_MARKER = "__mreactStaticBlock";
|
|
749
|
+
|
|
750
|
+
function isStaticReactiveBlockComponent(type: unknown): boolean {
|
|
751
|
+
return (
|
|
752
|
+
typeof type === "function" &&
|
|
753
|
+
(type as unknown as Record<string, unknown>)[STATIC_REACTIVE_BLOCK_MARKER] === true
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// In-place bailout for a keyed memo row whose props are unchanged and which has
|
|
758
|
+
// no hook/context/effect dependencies. The caller has proven the key order is
|
|
759
|
+
// unchanged (hasSameKeyOrderPrefix), so the fiber keeps its position and can be
|
|
760
|
+
// reused WITHOUT createWorkInProgress — no allocation, and the retained subtree
|
|
761
|
+
// (including any reactive-dom-block subscriptions) is left untouched. Returns
|
|
762
|
+
// undefined when the row must re-render or has dependencies (handled by the
|
|
763
|
+
// general per-child path instead). Mirrors tryReuseDependencyFreeMemoBailout +
|
|
764
|
+
// getMemoBailoutFiber's in-place branch, but is safe to reuse a fiber with
|
|
765
|
+
// disposable resources because a stable position means nothing is disposed.
|
|
766
|
+
function tryReuseDependencyFreeKeyedMemoRow(
|
|
767
|
+
matched: Fiber,
|
|
768
|
+
child: ReactCompatElement,
|
|
769
|
+
): Fiber | undefined {
|
|
770
|
+
if (
|
|
771
|
+
matched.tag !== "memo" ||
|
|
772
|
+
matched.type !== child.type ||
|
|
773
|
+
matched.hasRefSubtree === true ||
|
|
774
|
+
matched.hydrateExisting === true
|
|
775
|
+
) {
|
|
776
|
+
return undefined;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const state = matched.memoizedState as MemoFiberState | undefined;
|
|
780
|
+
|
|
781
|
+
if (
|
|
782
|
+
state === undefined ||
|
|
783
|
+
state.hasDirtyInstanceDependencies !== false ||
|
|
784
|
+
state.hasUnflushedEffectDependencies !== false ||
|
|
785
|
+
state.hasRetainedInstanceDependencies !== false ||
|
|
786
|
+
!areMemoPropsEqual(
|
|
787
|
+
child.type as { compare?: (a: Record<string, unknown>, b: Record<string, unknown>) => boolean },
|
|
788
|
+
state.props,
|
|
789
|
+
child.props as Record<string, unknown>,
|
|
790
|
+
)
|
|
791
|
+
) {
|
|
792
|
+
return undefined;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
matched.pendingProps = child.props;
|
|
796
|
+
matched.flags = NoFlags;
|
|
797
|
+
matched.subtreeFlags = NoFlags;
|
|
798
|
+
matched.childListChanged = false;
|
|
799
|
+
matched.subtreeChildListChanged = false;
|
|
800
|
+
matched.hostChildListChanged = false;
|
|
801
|
+
matched.child = getSkippedChild(matched);
|
|
802
|
+
return matched;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// In-place CHANGED-row update for a keyed memo row whose inner component is a
|
|
806
|
+
// compiler-marked static reactive block (returns its props verbatim as the block
|
|
807
|
+
// props). Instead of re-invoking the component to rebuild a throwaway block
|
|
808
|
+
// element, push the new props straight into the committed block's prop cell — the
|
|
809
|
+
// bound DOM updates via subscriptions, exactly as the normal block re-render path
|
|
810
|
+
// would, but without the memo/component render machinery. Same-order is proven by
|
|
811
|
+
// the caller, so the fiber is reused in place. Returns undefined when this is not
|
|
812
|
+
// a marked, dependency-free, single-block memo row (the general path handles it).
|
|
813
|
+
function tryCellUpdateStaticBlockMemoRow(
|
|
814
|
+
matched: Fiber,
|
|
815
|
+
child: ReactCompatElement,
|
|
816
|
+
): Fiber | undefined {
|
|
817
|
+
if (
|
|
818
|
+
matched.tag !== "memo" ||
|
|
819
|
+
matched.type !== child.type ||
|
|
820
|
+
matched.hasRefSubtree === true ||
|
|
821
|
+
matched.hydrateExisting === true ||
|
|
822
|
+
!isStaticReactiveBlockComponent((matched.type as { type?: unknown }).type)
|
|
823
|
+
) {
|
|
824
|
+
return undefined;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const state = matched.memoizedState as MemoFiberState | undefined;
|
|
828
|
+
|
|
829
|
+
if (
|
|
830
|
+
state === undefined ||
|
|
831
|
+
state.hasDirtyInstanceDependencies !== false ||
|
|
832
|
+
state.hasUnflushedEffectDependencies !== false ||
|
|
833
|
+
state.hasRetainedInstanceDependencies !== false
|
|
834
|
+
) {
|
|
835
|
+
return undefined;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// Navigate memo -> component fiber -> reactive-dom-block fiber. The marked
|
|
839
|
+
// component renders exactly one static block, so anything else is unexpected.
|
|
840
|
+
const componentFiber = matched.child;
|
|
841
|
+
if (componentFiber === undefined || componentFiber.sibling !== undefined) {
|
|
842
|
+
return undefined;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const blockFiber = componentFiber.child;
|
|
846
|
+
if (
|
|
847
|
+
blockFiber === undefined ||
|
|
848
|
+
blockFiber.tag !== "reactive-dom-block" ||
|
|
849
|
+
blockFiber.sibling !== undefined
|
|
850
|
+
) {
|
|
851
|
+
return undefined;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
const blockState = blockFiber.stateNode as ReactiveDomBlockState | undefined;
|
|
855
|
+
if (blockState?.propCell === undefined) {
|
|
856
|
+
return undefined;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// Drive the bound DOM through the prop cell (the marker guarantees the block's
|
|
860
|
+
// props are the component's props verbatim), then reuse the memo fiber in place.
|
|
861
|
+
setReactivePropCell(blockState.propCell, child.props as Record<string, unknown>);
|
|
862
|
+
matched.memoizedState = { ...state, props: child.props as Record<string, unknown> };
|
|
863
|
+
matched.pendingProps = child.props;
|
|
864
|
+
matched.flags = NoFlags;
|
|
865
|
+
matched.subtreeFlags = NoFlags;
|
|
866
|
+
matched.childListChanged = false;
|
|
867
|
+
matched.subtreeChildListChanged = false;
|
|
868
|
+
matched.hostChildListChanged = false;
|
|
869
|
+
matched.child = getSkippedChild(matched);
|
|
870
|
+
return matched;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Fast path for a keyed list of memo-wrapped rows (e.g. `<RowMemo key={id} />`)
|
|
874
|
+
// whose key order is UNCHANGED between renders — the js-framework-benchmark
|
|
875
|
+
// "select row" and "partial update" shapes. Walks current fibers and new
|
|
876
|
+
// children in lockstep: unchanged rows bail in place (no allocation, no general
|
|
877
|
+
// keyed-reconcile machinery), changed rows re-render through the proven
|
|
878
|
+
// per-child path. Any reorder / insert / delete / non-memo child / length change
|
|
879
|
+
// makes it return undefined so the general reconcile takes over.
|
|
880
|
+
function reconcileKeyedMemoRowHostChildren(
|
|
881
|
+
parent: Fiber,
|
|
882
|
+
currentFirstChild: Fiber | undefined,
|
|
883
|
+
children: readonly ReactCompatNode[],
|
|
884
|
+
runtime: RootRuntime | undefined,
|
|
885
|
+
path: string,
|
|
886
|
+
options: FiberHydrationOptions,
|
|
887
|
+
): FiberReconcileResult | undefined {
|
|
888
|
+
if (
|
|
889
|
+
children.length === 0 ||
|
|
890
|
+
currentFirstChild === undefined ||
|
|
891
|
+
runtime === undefined ||
|
|
892
|
+
options.previousNodes !== undefined ||
|
|
893
|
+
!isKeyedMemoRowCandidate(children[0]) ||
|
|
894
|
+
!hasSameKeyOrderPrefix(currentFirstChild, children)
|
|
895
|
+
) {
|
|
896
|
+
return undefined;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
let currentKeyed: Fiber | undefined = currentFirstChild;
|
|
900
|
+
let first: Fiber | undefined;
|
|
901
|
+
let previous: Fiber | undefined;
|
|
902
|
+
let subtreeFlags = NoFlags;
|
|
903
|
+
let subtreeChildListChanged = false;
|
|
904
|
+
let hasRefSubtree = false;
|
|
905
|
+
let hasDisposableResources = false;
|
|
906
|
+
let dirtyChildren: Fiber[] | undefined;
|
|
907
|
+
|
|
908
|
+
for (let index = 0; index < children.length; index += 1) {
|
|
909
|
+
const child = children[index];
|
|
910
|
+
|
|
911
|
+
if (currentKeyed === undefined || !isKeyedMemoRowCandidate(child)) {
|
|
912
|
+
return undefined;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
const matched = currentKeyed;
|
|
916
|
+
currentKeyed = currentKeyed.sibling;
|
|
917
|
+
const childElement = child as ReactCompatElement;
|
|
918
|
+
|
|
919
|
+
let fiber =
|
|
920
|
+
tryReuseDependencyFreeKeyedMemoRow(matched, childElement) ??
|
|
921
|
+
tryCellUpdateStaticBlockMemoRow(matched, childElement);
|
|
922
|
+
if (fiber === undefined) {
|
|
923
|
+
const result = createHostFiber(
|
|
924
|
+
parent,
|
|
925
|
+
matched,
|
|
926
|
+
child,
|
|
927
|
+
childElement.key ?? undefined,
|
|
928
|
+
runtime,
|
|
929
|
+
getReconcileChildPath(path, child, index, options),
|
|
930
|
+
options,
|
|
931
|
+
false,
|
|
932
|
+
);
|
|
933
|
+
fiber = result.fiber;
|
|
934
|
+
|
|
935
|
+
if (fiber === undefined) {
|
|
936
|
+
return undefined;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
if (fiber !== matched && fiber.alternate !== matched) {
|
|
940
|
+
markOptimizedChildForDeletion(parent, matched);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (first === undefined) {
|
|
945
|
+
first = fiber;
|
|
946
|
+
} else if (previous !== undefined) {
|
|
947
|
+
previous.sibling = fiber;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
fiber.return = parent;
|
|
951
|
+
fiber.sibling = undefined;
|
|
952
|
+
if (fiber.hasRefSubtree) {
|
|
953
|
+
hasRefSubtree = true;
|
|
954
|
+
}
|
|
955
|
+
if (fiber.hasDisposableResources) {
|
|
956
|
+
hasDisposableResources = true;
|
|
957
|
+
}
|
|
958
|
+
subtreeFlags |= fiber.flags | fiber.subtreeFlags;
|
|
959
|
+
subtreeChildListChanged =
|
|
960
|
+
subtreeChildListChanged || fiber.childListChanged || fiber.subtreeChildListChanged;
|
|
961
|
+
if (
|
|
962
|
+
fiber.tag === "memo" &&
|
|
963
|
+
(fiber.stateNode === undefined || typeof fiber.stateNode === "number")
|
|
964
|
+
) {
|
|
965
|
+
fiber.stateNode = index;
|
|
966
|
+
}
|
|
967
|
+
if (hasHostCommitWork(fiber)) {
|
|
968
|
+
(dirtyChildren ??= []).push(fiber);
|
|
969
|
+
}
|
|
970
|
+
previous = fiber;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
if (currentKeyed !== undefined) {
|
|
974
|
+
return undefined;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
parent.hasRefSubtree = hasRefSubtree;
|
|
978
|
+
parent.hasDisposableResources = hasDisposableResources;
|
|
979
|
+
parent.subtreeFlags = subtreeFlags;
|
|
980
|
+
parent.subtreeChildListChanged = subtreeChildListChanged;
|
|
981
|
+
parent.childListChanged = false;
|
|
982
|
+
recordDirtyChildCommitHints(parent, dirtyChildren);
|
|
983
|
+
return { fiber: first, consumed: 0 };
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
function canReuseDependencyFreeMemoAtKey(
|
|
987
|
+
current: Fiber | undefined,
|
|
988
|
+
node: ReactCompatElement,
|
|
989
|
+
key: string,
|
|
990
|
+
): boolean {
|
|
991
|
+
return (
|
|
992
|
+
current !== undefined &&
|
|
993
|
+
current.key === key &&
|
|
994
|
+
current.tag === "memo" &&
|
|
995
|
+
current.type === node.type &&
|
|
996
|
+
current.hasRefSubtree !== true &&
|
|
997
|
+
current.hasDisposableResources !== true &&
|
|
998
|
+
current.hydrateExisting !== true &&
|
|
999
|
+
isMemoType(node.type)
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function tryReuseDependencyFreeMemoRemovalSuffix(
|
|
1004
|
+
parent: Fiber,
|
|
1005
|
+
children: readonly ReactCompatNode[],
|
|
1006
|
+
startIndex: number,
|
|
1007
|
+
removed: Fiber,
|
|
1008
|
+
matchedCurrent: Fiber,
|
|
1009
|
+
runtime: RootRuntime | undefined,
|
|
1010
|
+
options: FiberHydrationOptions,
|
|
1011
|
+
prefixFirst: Fiber | undefined,
|
|
1012
|
+
prefixPrevious: Fiber | undefined,
|
|
1013
|
+
consumed: number,
|
|
1014
|
+
): FiberReconcileResult | undefined {
|
|
1015
|
+
if (
|
|
1016
|
+
runtime === undefined ||
|
|
1017
|
+
options.previousNodes !== undefined ||
|
|
1018
|
+
removed.hasRefSubtree
|
|
1019
|
+
) {
|
|
1020
|
+
return undefined;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
const suffix: Fiber[] = [];
|
|
1024
|
+
let current: Fiber | undefined = matchedCurrent;
|
|
1025
|
+
|
|
1026
|
+
for (let index = startIndex; index < children.length; index += 1) {
|
|
1027
|
+
const child = children[index];
|
|
1028
|
+
const key = getNodeKey(child);
|
|
1029
|
+
|
|
1030
|
+
if (key === undefined || !isReactCompatElement(child) || !isMemoType(child.type)) {
|
|
1031
|
+
return undefined;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
if (!canReuseDependencyFreeMemoAtKey(current, child, key)) {
|
|
1035
|
+
return undefined;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
if (current === undefined) {
|
|
1039
|
+
return undefined;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
const matched: Fiber = current;
|
|
1043
|
+
const fiber = reuseDependencyFreeMemoFiber(matched, child);
|
|
1044
|
+
|
|
1045
|
+
if (fiber === undefined) {
|
|
1046
|
+
return undefined;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
suffix.push(fiber);
|
|
1050
|
+
current = matched.sibling;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
if (current !== undefined) {
|
|
1054
|
+
return undefined;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
let first = prefixFirst;
|
|
1058
|
+
let previous = prefixPrevious;
|
|
1059
|
+
|
|
1060
|
+
for (const fiber of suffix) {
|
|
1061
|
+
if (first === undefined) {
|
|
1062
|
+
first = fiber;
|
|
1063
|
+
} else if (previous !== undefined) {
|
|
1064
|
+
previous.sibling = fiber;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
fiber.return = parent;
|
|
1068
|
+
fiber.sibling = undefined;
|
|
1069
|
+
previous = fiber;
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
parent.childListChanged = true;
|
|
1073
|
+
parent.deletions = [removed];
|
|
1074
|
+
markOptimizedChildForDeletion(parent, removed);
|
|
1075
|
+
return { fiber: first, consumed };
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
function reuseDependencyFreeMemoFiber(
|
|
1079
|
+
current: Fiber,
|
|
1080
|
+
node: ReactCompatElement,
|
|
1081
|
+
): Fiber | undefined {
|
|
1082
|
+
if (!isMemoType(node.type)) {
|
|
1083
|
+
return undefined;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
const previousMemoState = current.memoizedState as MemoFiberState | undefined;
|
|
1087
|
+
|
|
1088
|
+
if (
|
|
1089
|
+
previousMemoState === undefined ||
|
|
1090
|
+
previousMemoState.hasDirtyInstanceDependencies !== false ||
|
|
1091
|
+
previousMemoState.hasUnflushedEffectDependencies !== false ||
|
|
1092
|
+
previousMemoState.hasRetainedInstanceDependencies !== false ||
|
|
1093
|
+
!areMemoPropsEqual(node.type, previousMemoState.props, node.props)
|
|
1094
|
+
) {
|
|
1095
|
+
return undefined;
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
current.pendingProps = node.props;
|
|
1099
|
+
current.flags = NoFlags;
|
|
1100
|
+
current.subtreeFlags = NoFlags;
|
|
1101
|
+
current.childListChanged = false;
|
|
1102
|
+
current.subtreeChildListChanged = false;
|
|
1103
|
+
current.hostChildListChanged = false;
|
|
1104
|
+
current.type = node.type;
|
|
1105
|
+
current.child = getSkippedChild(current);
|
|
1106
|
+
current.memoizedState = previousMemoState;
|
|
1107
|
+
return current;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
622
1110
|
function canStoreAppendSuffixCommitHint(parent: Fiber): boolean {
|
|
623
1111
|
return (
|
|
624
1112
|
parent.tag === "fragment" ||
|
|
@@ -712,6 +1200,7 @@ function getReusableKeyedRowHostFiber(
|
|
|
712
1200
|
current.subtreeChildListChanged = false;
|
|
713
1201
|
current.hostChildListChanged = false;
|
|
714
1202
|
current.hasRefSubtree = false;
|
|
1203
|
+
current.hasDisposableResources = false;
|
|
715
1204
|
return current;
|
|
716
1205
|
}
|
|
717
1206
|
|
|
@@ -793,6 +1282,7 @@ function createKeyedRowHostFiber(
|
|
|
793
1282
|
fiber.pendingProps = node.props;
|
|
794
1283
|
fiber.hostChildListChanged = false;
|
|
795
1284
|
fiber.hasRefSubtree = false;
|
|
1285
|
+
fiber.hasDisposableResources = false;
|
|
796
1286
|
|
|
797
1287
|
if (current === undefined || fiber.alternate !== current) {
|
|
798
1288
|
fiber.flags |= Placement;
|
|
@@ -956,6 +1446,9 @@ function bubbleHostChild(parent: Fiber, child: Fiber): void {
|
|
|
956
1446
|
if (child.hasRefSubtree) {
|
|
957
1447
|
parent.hasRefSubtree = true;
|
|
958
1448
|
}
|
|
1449
|
+
if (child.hasDisposableResources) {
|
|
1450
|
+
parent.hasDisposableResources = true;
|
|
1451
|
+
}
|
|
959
1452
|
parent.subtreeFlags |= child.flags | child.subtreeFlags;
|
|
960
1453
|
parent.subtreeChildListChanged =
|
|
961
1454
|
parent.subtreeChildListChanged ||
|
|
@@ -963,8 +1456,21 @@ function bubbleHostChild(parent: Fiber, child: Fiber): void {
|
|
|
963
1456
|
child.subtreeChildListChanged;
|
|
964
1457
|
}
|
|
965
1458
|
|
|
1459
|
+
function recordDirtyChildCommitHints(
|
|
1460
|
+
parent: Fiber,
|
|
1461
|
+
dirtyChildren: Fiber[] | undefined,
|
|
1462
|
+
): void {
|
|
1463
|
+
if (parent.childListChanged || dirtyChildren === undefined || dirtyChildren.length === 0) {
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// Reuse the effect-list slot only when there are no child-list deletions.
|
|
1468
|
+
parent.deletions = dirtyChildren;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
966
1471
|
function resetFiberRefSubtree(fiber: Fiber): void {
|
|
967
1472
|
fiber.hasRefSubtree = false;
|
|
1473
|
+
fiber.hasDisposableResources = false;
|
|
968
1474
|
}
|
|
969
1475
|
|
|
970
1476
|
function includeNodeRef(fiber: Fiber, node: ReactCompatNode): void {
|
|
@@ -1142,6 +1648,13 @@ function createHostFiberImpl(
|
|
|
1142
1648
|
return { fiber: undefined, consumed: 0 };
|
|
1143
1649
|
}
|
|
1144
1650
|
|
|
1651
|
+
// Host elements (string type) are by far the most common node. Dispatch them
|
|
1652
|
+
// before the component-type checks below, none of which a string can match,
|
|
1653
|
+
// so each reconciled host element skips ~12 type comparisons / probes.
|
|
1654
|
+
if (typeof node.type === "string") {
|
|
1655
|
+
return createHostComponentFiber(parent, current, node, key, runtime, path, options);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1145
1658
|
if (node.type === Fragment) {
|
|
1146
1659
|
const fiber =
|
|
1147
1660
|
current?.tag === "fragment"
|
|
@@ -1269,6 +1782,37 @@ function createHostFiberImpl(
|
|
|
1269
1782
|
return { fiber, consumed: options.previousNodes?.length ?? 0 };
|
|
1270
1783
|
}
|
|
1271
1784
|
|
|
1785
|
+
if (node.type === REACTIVE_DOM_BLOCK_TYPE) {
|
|
1786
|
+
const blockProps = (node.props as unknown as ReactiveDomBlockProps).blockProps;
|
|
1787
|
+
if (current?.tag === "reactive-dom-block") {
|
|
1788
|
+
// Re-render: reuse the committed DOM/subscriptions and push the new props
|
|
1789
|
+
// into the prop cell instead of re-running render(). Bound text/attributes
|
|
1790
|
+
// update via their reactive subscriptions; the subtree is never reconciled.
|
|
1791
|
+
const fiber = createWorkInProgress(current, node.props);
|
|
1792
|
+
fiber.type = node.type;
|
|
1793
|
+
fiber.hasDisposableResources = true;
|
|
1794
|
+
const previousState = current.stateNode as ReactiveDomBlockState | undefined;
|
|
1795
|
+
if (previousState?.propCell !== undefined && blockProps !== undefined) {
|
|
1796
|
+
setReactivePropCell(previousState.propCell, blockProps);
|
|
1797
|
+
}
|
|
1798
|
+
fiber.stateNode = previousState;
|
|
1799
|
+
return { fiber, consumed: options.previousNodes?.length ?? 0 };
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
const fiber = createFiber("reactive-dom-block", node.props, key);
|
|
1803
|
+
fiber.type = node.type;
|
|
1804
|
+
fiber.hasDisposableResources = true;
|
|
1805
|
+
const render = (node.props as unknown as ReactiveDomBlockProps).render;
|
|
1806
|
+
if (blockProps !== undefined) {
|
|
1807
|
+
const propCell = createReactivePropCell(blockProps);
|
|
1808
|
+
const result = render(createReactivePropProxy(propCell));
|
|
1809
|
+
fiber.stateNode = { node: result.node, dispose: result.dispose, propCell };
|
|
1810
|
+
} else {
|
|
1811
|
+
fiber.stateNode = (render as () => ReactiveDomBlockResult)();
|
|
1812
|
+
}
|
|
1813
|
+
return { fiber, consumed: options.previousNodes?.length ?? 0 };
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1272
1816
|
if (isReactCompatProvider(node.type)) {
|
|
1273
1817
|
const fiber =
|
|
1274
1818
|
current?.tag === "context-provider" && current.type === node.type
|
|
@@ -1390,8 +1934,11 @@ function createHostFiberImpl(
|
|
|
1390
1934
|
fiber.type = memoType;
|
|
1391
1935
|
|
|
1392
1936
|
const renderedElement: ReactCompatElement = {
|
|
1393
|
-
|
|
1937
|
+
$$typeof: node.$$typeof,
|
|
1394
1938
|
type: memoType.type,
|
|
1939
|
+
key: node.key,
|
|
1940
|
+
ref: node.ref,
|
|
1941
|
+
props: node.props,
|
|
1395
1942
|
};
|
|
1396
1943
|
const childResult = createHostFiber(
|
|
1397
1944
|
fiber,
|
|
@@ -1408,7 +1955,7 @@ function createHostFiberImpl(
|
|
|
1408
1955
|
fiber.child.sibling = undefined;
|
|
1409
1956
|
bubbleHostChild(fiber, fiber.child);
|
|
1410
1957
|
}
|
|
1411
|
-
const instanceKeys =
|
|
1958
|
+
const instanceKeys = collectMemoInstanceKeys(runtime, memoPath);
|
|
1412
1959
|
const hasClassDescendant = hasClassComponentDescendant(fiber.child);
|
|
1413
1960
|
fiber.memoizedState = {
|
|
1414
1961
|
props: node.props as Record<string, unknown>,
|
|
@@ -1439,8 +1986,11 @@ function createHostFiberImpl(
|
|
|
1439
1986
|
|
|
1440
1987
|
if (lazyType.status === "resolved" && lazyType.resolved !== undefined) {
|
|
1441
1988
|
const renderedElement: ReactCompatElement = {
|
|
1442
|
-
|
|
1989
|
+
$$typeof: node.$$typeof,
|
|
1443
1990
|
type: lazyType.resolved,
|
|
1991
|
+
key: node.key,
|
|
1992
|
+
ref: node.ref,
|
|
1993
|
+
props: node.props,
|
|
1444
1994
|
};
|
|
1445
1995
|
const childResult = createHostFiber(
|
|
1446
1996
|
fiber,
|
|
@@ -1625,61 +2175,95 @@ function createHostFiberImpl(
|
|
|
1625
2175
|
const instanceKeys = collectInstanceKeys(runtime, path);
|
|
1626
2176
|
fiber.stateNode = {
|
|
1627
2177
|
element: node,
|
|
1628
|
-
props:
|
|
2178
|
+
props: node.props as Record<string, unknown>,
|
|
1629
2179
|
instanceKeys,
|
|
1630
2180
|
hasContextDependencies: hasContextDependency(runtime, instanceKeys),
|
|
1631
2181
|
} satisfies FunctionFiberState;
|
|
1632
2182
|
return { fiber, consumed: childResult.consumed };
|
|
1633
2183
|
}
|
|
1634
2184
|
|
|
2185
|
+
return { fiber: undefined, consumed: 0 };
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
// The host-component reconcile, split out of createHostFiberImpl so host
|
|
2189
|
+
// elements can be dispatched before the component-type checks. Only called with
|
|
2190
|
+
// a string element type.
|
|
2191
|
+
function createHostComponentFiber(
|
|
2192
|
+
parent: Fiber,
|
|
2193
|
+
current: Fiber | undefined,
|
|
2194
|
+
node: ReactCompatElement,
|
|
2195
|
+
key: string | undefined,
|
|
2196
|
+
runtime: RootRuntime | undefined,
|
|
2197
|
+
path: string,
|
|
2198
|
+
options: FiberHydrationOptions,
|
|
2199
|
+
): FiberReconcileResult {
|
|
1635
2200
|
if (typeof node.type !== "string") {
|
|
1636
2201
|
return { fiber: undefined, consumed: 0 };
|
|
1637
2202
|
}
|
|
1638
2203
|
|
|
2204
|
+
const initialHostOnlyFiber = tryCreateInitialHostOnlyFiber(
|
|
2205
|
+
current,
|
|
2206
|
+
node,
|
|
2207
|
+
key,
|
|
2208
|
+
options,
|
|
2209
|
+
);
|
|
2210
|
+
if (initialHostOnlyFiber !== undefined) {
|
|
2211
|
+
return { fiber: initialHostOnlyFiber, consumed: 0 };
|
|
2212
|
+
}
|
|
2213
|
+
|
|
1639
2214
|
const elementNamespace = namespaceForHostElement(options.namespace ?? "html", node.type);
|
|
1640
2215
|
const childNamespace = namespaceForHostChildren(elementNamespace, node.type);
|
|
2216
|
+
const reusableCurrent =
|
|
2217
|
+
current?.tag === "host-component" && current.type === node.type ? current : undefined;
|
|
1641
2218
|
const fiber =
|
|
1642
|
-
|
|
1643
|
-
? createWorkInProgress(
|
|
2219
|
+
reusableCurrent !== undefined
|
|
2220
|
+
? createWorkInProgress(reusableCurrent, node.props)
|
|
1644
2221
|
: createFiber("host-component", node.props, key);
|
|
2222
|
+
// The hydration node bookkeeping only matters when hydrating. Skipping it for
|
|
2223
|
+
// the common (non-hydration) render avoids an isHostElement(undefined) probe
|
|
2224
|
+
// and the mismatch checks on every reconciled element.
|
|
1645
2225
|
const existing = options.previousNodes?.[0];
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
2226
|
+
let existingElement: HostElement | undefined;
|
|
2227
|
+
let tagMatches = false;
|
|
2228
|
+
if (options.previousNodes !== undefined) {
|
|
2229
|
+
existingElement = isHostElement(existing) ? existing : undefined;
|
|
2230
|
+
tagMatches =
|
|
2231
|
+
existingElement !== undefined &&
|
|
2232
|
+
hostElementMatches(existingElement, node.type, elementNamespace);
|
|
2233
|
+
|
|
2234
|
+
if (existing === undefined) {
|
|
2235
|
+
reportMissingHydrationNode(options, path);
|
|
2236
|
+
} else if (!isHostElement(existing)) {
|
|
2237
|
+
reportHydrationNodeTypeMismatch(options, path, `<${node.type}>`, existing);
|
|
2238
|
+
}
|
|
1656
2239
|
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
2240
|
+
if (existingElement !== undefined && !tagMatches) {
|
|
2241
|
+
reportRecoverable(
|
|
2242
|
+
options,
|
|
2243
|
+
"tag",
|
|
2244
|
+
path,
|
|
2245
|
+
new Error(
|
|
2246
|
+
`Hydration tag mismatch: expected <${node.type}> but found <${existingElement.tagName.toLowerCase()}>.`,
|
|
2247
|
+
),
|
|
2248
|
+
);
|
|
2249
|
+
reportElementTextMismatch(options, `${path}.c`, existingElement, node.props.children);
|
|
2250
|
+
}
|
|
1667
2251
|
}
|
|
1668
2252
|
|
|
1669
2253
|
fiber.type = node.type;
|
|
2254
|
+
// When reusing a same-type current fiber, its stateNode was created for this
|
|
2255
|
+
// exact tag and namespace (same tree position), so the hostElementMatches
|
|
2256
|
+
// re-check (localName + namespaceURI reads) is redundant.
|
|
1670
2257
|
fiber.stateNode =
|
|
1671
2258
|
tagMatches
|
|
1672
2259
|
? existingElement
|
|
1673
|
-
:
|
|
1674
|
-
|
|
1675
|
-
isHostElement(current.stateNode) &&
|
|
1676
|
-
hostElementMatches(current.stateNode, node.type, elementNamespace)
|
|
1677
|
-
? current.stateNode
|
|
2260
|
+
: reusableCurrent !== undefined && isHostElement(reusableCurrent.stateNode)
|
|
2261
|
+
? reusableCurrent.stateNode
|
|
1678
2262
|
: createHostElement(getDocumentRef(options), node.type, options.namespace ?? "html");
|
|
1679
|
-
fiber.hydrateExisting = tagMatches
|
|
2263
|
+
fiber.hydrateExisting = tagMatches;
|
|
1680
2264
|
const previousChildNodes =
|
|
1681
2265
|
tagMatches && existingElement !== undefined
|
|
1682
|
-
? Array.from(existingElement.childNodes)
|
|
2266
|
+
? Array.from((existingElement as Element).childNodes)
|
|
1683
2267
|
: undefined;
|
|
1684
2268
|
const directTextChild =
|
|
1685
2269
|
shouldUseDirectHostTextChild() && previousChildNodes === undefined
|
|
@@ -1713,11 +2297,7 @@ function createHostFiberImpl(
|
|
|
1713
2297
|
node.props.children as ReactCompatNode,
|
|
1714
2298
|
runtime,
|
|
1715
2299
|
`${path}.c`,
|
|
1716
|
-
|
|
1717
|
-
...options,
|
|
1718
|
-
namespace: childNamespace,
|
|
1719
|
-
...(previousChildNodes === undefined ? {} : { previousNodes: previousChildNodes }),
|
|
1720
|
-
},
|
|
2300
|
+
getHostChildFiberOptions(options, childNamespace, previousChildNodes),
|
|
1721
2301
|
);
|
|
1722
2302
|
fiber.child = childResult.fiber;
|
|
1723
2303
|
if (previousChildNodes !== undefined) {
|
|
@@ -1737,6 +2317,185 @@ function isFunctionComponentType(value: unknown): value is (
|
|
|
1737
2317
|
);
|
|
1738
2318
|
}
|
|
1739
2319
|
|
|
2320
|
+
function tryCreateInitialHostOnlyFiber(
|
|
2321
|
+
current: Fiber | undefined,
|
|
2322
|
+
node: ReactCompatElement,
|
|
2323
|
+
key: string | undefined,
|
|
2324
|
+
options: FiberHydrationOptions,
|
|
2325
|
+
): Fiber | undefined {
|
|
2326
|
+
if (
|
|
2327
|
+
current !== undefined ||
|
|
2328
|
+
options.previousNodes !== undefined ||
|
|
2329
|
+
!shouldUseDirectHostTextChild() ||
|
|
2330
|
+
typeof node.type !== "string" ||
|
|
2331
|
+
!canCreateInitialHostOnlyNode(node)
|
|
2332
|
+
) {
|
|
2333
|
+
return undefined;
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2336
|
+
return createInitialHostOnlyElementFiber(
|
|
2337
|
+
node,
|
|
2338
|
+
key,
|
|
2339
|
+
options.namespace ?? "html",
|
|
2340
|
+
getDocumentRef(options),
|
|
2341
|
+
);
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
function createInitialHostOnlyElementFiber(
|
|
2345
|
+
node: ReactCompatElement,
|
|
2346
|
+
key: string | undefined,
|
|
2347
|
+
namespace: HostNamespace,
|
|
2348
|
+
documentRef: Document | CustomHostDocument,
|
|
2349
|
+
): Fiber {
|
|
2350
|
+
const elementType = node.type as string;
|
|
2351
|
+
const elementNamespace = namespaceForHostElement(namespace, elementType);
|
|
2352
|
+
const childNamespace = namespaceForHostChildren(elementNamespace, elementType);
|
|
2353
|
+
const fiber = createFiber("host-component", node.props, key);
|
|
2354
|
+
fiber.type = elementType;
|
|
2355
|
+
fiber.stateNode = createHostElement(documentRef, elementType, namespace);
|
|
2356
|
+
fiber.flags |= Placement;
|
|
2357
|
+
fiber.hostChildListChanged = true;
|
|
2358
|
+
|
|
2359
|
+
if (getDirectHostTextChild(node.props.children) === undefined) {
|
|
2360
|
+
fiber.child = createInitialHostOnlyChildList(
|
|
2361
|
+
fiber,
|
|
2362
|
+
node.props.children as ReactCompatNode,
|
|
2363
|
+
childNamespace,
|
|
2364
|
+
documentRef,
|
|
2365
|
+
);
|
|
2366
|
+
}
|
|
2367
|
+
|
|
2368
|
+
return fiber;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
function createInitialHostOnlyChildList(
|
|
2372
|
+
parent: Fiber,
|
|
2373
|
+
children: ReactCompatNode,
|
|
2374
|
+
namespace: HostNamespace,
|
|
2375
|
+
documentRef: Document | CustomHostDocument,
|
|
2376
|
+
): Fiber | undefined {
|
|
2377
|
+
const normalized = normalizeChildren(children);
|
|
2378
|
+
let first: Fiber | undefined;
|
|
2379
|
+
let previous: Fiber | undefined;
|
|
2380
|
+
|
|
2381
|
+
for (let index = 0; index < normalized.length; index += 1) {
|
|
2382
|
+
const child = normalized[index];
|
|
2383
|
+
const fiber = createInitialHostOnlyChildFiber(child, namespace, documentRef);
|
|
2384
|
+
|
|
2385
|
+
if (fiber === undefined) {
|
|
2386
|
+
continue;
|
|
2387
|
+
}
|
|
2388
|
+
|
|
2389
|
+
fiber.return = parent;
|
|
2390
|
+
fiber.memoizedState = index;
|
|
2391
|
+
|
|
2392
|
+
if (first === undefined) {
|
|
2393
|
+
first = fiber;
|
|
2394
|
+
} else if (previous !== undefined) {
|
|
2395
|
+
previous.sibling = fiber;
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
bubbleHostChild(parent, fiber);
|
|
2399
|
+
previous = fiber;
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
return first;
|
|
2403
|
+
}
|
|
2404
|
+
|
|
2405
|
+
function createInitialHostOnlyChildFiber(
|
|
2406
|
+
node: ReactCompatNode,
|
|
2407
|
+
namespace: HostNamespace,
|
|
2408
|
+
documentRef: Document | CustomHostDocument,
|
|
2409
|
+
): Fiber | undefined {
|
|
2410
|
+
if (node === null || node === undefined || typeof node === "boolean") {
|
|
2411
|
+
return undefined;
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
if (typeof node === "string" || typeof node === "number") {
|
|
2415
|
+
const fiber = createFiber("host-text", String(node));
|
|
2416
|
+
fiber.stateNode = createHostTextNode(documentRef);
|
|
2417
|
+
fiber.flags |= Placement;
|
|
2418
|
+
return fiber;
|
|
2419
|
+
}
|
|
2420
|
+
|
|
2421
|
+
if (Array.isArray(node)) {
|
|
2422
|
+
const fiber = createFiber("fragment", node);
|
|
2423
|
+
fiber.child = createInitialHostOnlyChildList(fiber, node, namespace, documentRef);
|
|
2424
|
+
return fiber.child === undefined ? undefined : fiber;
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
if (!isReactCompatElement(node) || typeof node.type !== "string") {
|
|
2428
|
+
return undefined;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
return createInitialHostOnlyElementFiber(
|
|
2432
|
+
node,
|
|
2433
|
+
node.key === null ? undefined : node.key,
|
|
2434
|
+
namespace,
|
|
2435
|
+
documentRef,
|
|
2436
|
+
);
|
|
2437
|
+
}
|
|
2438
|
+
|
|
2439
|
+
function canCreateInitialHostOnlyNode(node: ReactCompatNode): boolean {
|
|
2440
|
+
if (
|
|
2441
|
+
node === null ||
|
|
2442
|
+
node === undefined ||
|
|
2443
|
+
typeof node === "boolean" ||
|
|
2444
|
+
typeof node === "string" ||
|
|
2445
|
+
typeof node === "number"
|
|
2446
|
+
) {
|
|
2447
|
+
return true;
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2450
|
+
if (Array.isArray(node)) {
|
|
2451
|
+
return node.every(canCreateInitialHostOnlyNode);
|
|
2452
|
+
}
|
|
2453
|
+
|
|
2454
|
+
if (
|
|
2455
|
+
!isReactCompatElement(node) ||
|
|
2456
|
+
typeof node.type !== "string" ||
|
|
2457
|
+
node.ref !== null ||
|
|
2458
|
+
hasInitialHostOnlyExcludedProps(node.props)
|
|
2459
|
+
) {
|
|
2460
|
+
return false;
|
|
2461
|
+
}
|
|
2462
|
+
|
|
2463
|
+
return canCreateInitialHostOnlyNode(node.props.children as ReactCompatNode);
|
|
2464
|
+
}
|
|
2465
|
+
|
|
2466
|
+
function hasInitialHostOnlyExcludedProps(props: Record<string, unknown>): boolean {
|
|
2467
|
+
return (
|
|
2468
|
+
hasOwnProperty.call(props, REACTIVE_TEXT_BINDING_META) ||
|
|
2469
|
+
props.dangerouslySetInnerHTML !== undefined ||
|
|
2470
|
+
props.contentEditable === true ||
|
|
2471
|
+
props.suppressContentEditableWarning === true ||
|
|
2472
|
+
props.value !== undefined ||
|
|
2473
|
+
props.defaultValue !== undefined ||
|
|
2474
|
+
props.checked !== undefined ||
|
|
2475
|
+
props.defaultChecked !== undefined
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
|
|
2479
|
+
function getHostChildFiberOptions(
|
|
2480
|
+
options: FiberHydrationOptions,
|
|
2481
|
+
namespace: HostNamespace,
|
|
2482
|
+
previousNodes: readonly Node[] | undefined,
|
|
2483
|
+
): FiberHydrationOptions {
|
|
2484
|
+
const namespaceUnchanged =
|
|
2485
|
+
options.namespace === namespace ||
|
|
2486
|
+
(options.namespace === undefined && namespace === "html");
|
|
2487
|
+
|
|
2488
|
+
if (previousNodes === undefined && namespaceUnchanged) {
|
|
2489
|
+
return options;
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
return {
|
|
2493
|
+
...options,
|
|
2494
|
+
namespace,
|
|
2495
|
+
...(previousNodes === undefined ? {} : { previousNodes }),
|
|
2496
|
+
};
|
|
2497
|
+
}
|
|
2498
|
+
|
|
1740
2499
|
function commitHostChildren(
|
|
1741
2500
|
fiber: Fiber | undefined,
|
|
1742
2501
|
parent: ParentNode,
|
|
@@ -1778,6 +2537,49 @@ function commitHostDirtyChildren(
|
|
|
1778
2537
|
}
|
|
1779
2538
|
}
|
|
1780
2539
|
|
|
2540
|
+
function commitHostDirtyChildrenOf(
|
|
2541
|
+
owner: Fiber,
|
|
2542
|
+
fiber: Fiber | undefined,
|
|
2543
|
+
parent: ParentNode,
|
|
2544
|
+
eventRoot: Element,
|
|
2545
|
+
path: string,
|
|
2546
|
+
options: RenderOptions = {},
|
|
2547
|
+
): void {
|
|
2548
|
+
const dirtyChildren = readDirtyChildCommitHints(owner);
|
|
2549
|
+
|
|
2550
|
+
if (dirtyChildren === undefined) {
|
|
2551
|
+
commitHostDirtyChildren(fiber, parent, eventRoot, path, options);
|
|
2552
|
+
return;
|
|
2553
|
+
}
|
|
2554
|
+
|
|
2555
|
+
for (let index = 0; index < dirtyChildren.length; index += 1) {
|
|
2556
|
+
const dirtyChild = dirtyChildren[index];
|
|
2557
|
+
|
|
2558
|
+
if (dirtyChild !== undefined && hasHostCommitWork(dirtyChild)) {
|
|
2559
|
+
commitHostDirtyFiber(
|
|
2560
|
+
dirtyChild,
|
|
2561
|
+
parent,
|
|
2562
|
+
eventRoot,
|
|
2563
|
+
joinCommitPath(path, String(getDirtyChildCommitIndex(dirtyChild, index))),
|
|
2564
|
+
options,
|
|
2565
|
+
);
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
owner.deletions = undefined;
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2571
|
+
function readDirtyChildCommitHints(fiber: Fiber): Fiber[] | undefined {
|
|
2572
|
+
return fiber.childListChanged ? undefined : fiber.deletions;
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
function getDirtyChildCommitIndex(fiber: Fiber, fallback: number): number {
|
|
2576
|
+
if (typeof fiber.memoizedState === "number") {
|
|
2577
|
+
return fiber.memoizedState;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
return typeof fiber.stateNode === "number" ? fiber.stateNode : fallback;
|
|
2581
|
+
}
|
|
2582
|
+
|
|
1781
2583
|
function commitHostDirtyFiber(
|
|
1782
2584
|
fiber: Fiber,
|
|
1783
2585
|
parent: ParentNode,
|
|
@@ -1798,6 +2600,7 @@ function commitHostDirtyFiber(
|
|
|
1798
2600
|
return;
|
|
1799
2601
|
}
|
|
1800
2602
|
|
|
2603
|
+
const isDomElement = isDomHostElement(element);
|
|
1801
2604
|
const props = fiber.pendingProps as Record<string, unknown>;
|
|
1802
2605
|
const previousProps = fiber.memoizedProps as Record<string, unknown> | undefined;
|
|
1803
2606
|
const directTextChild =
|
|
@@ -1823,7 +2626,7 @@ function commitHostDirtyFiber(
|
|
|
1823
2626
|
fiber.hydrateExisting !== true &&
|
|
1824
2627
|
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
1825
2628
|
|
|
1826
|
-
if (
|
|
2629
|
+
if (isDomElement && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
1827
2630
|
applyProps(element, props, path, {
|
|
1828
2631
|
...options,
|
|
1829
2632
|
eventRoot,
|
|
@@ -1841,16 +2644,16 @@ function commitHostDirtyFiber(
|
|
|
1841
2644
|
) {
|
|
1842
2645
|
const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1843
2646
|
if (
|
|
1844
|
-
!(
|
|
1845
|
-
!(
|
|
2647
|
+
!(isDomElement && childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
2648
|
+
!(isDomElement && shouldPreserveContentEditableChildren(element, props, childNodes))
|
|
1846
2649
|
) {
|
|
1847
2650
|
syncChildNodes(element as ParentNode, childNodes);
|
|
1848
2651
|
}
|
|
1849
2652
|
} else if (fiber.subtreeFlags !== NoFlags) {
|
|
1850
|
-
|
|
2653
|
+
commitHostDirtyChildrenOf(fiber, fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1851
2654
|
}
|
|
1852
2655
|
|
|
1853
|
-
if (
|
|
2656
|
+
if (isDomElement && isFormHostType(fiber.type)) {
|
|
1854
2657
|
applyPostChildFormProps(element, props, previousProps);
|
|
1855
2658
|
}
|
|
1856
2659
|
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
@@ -1890,7 +2693,8 @@ function commitHostDirtyFiber(
|
|
|
1890
2693
|
syncOwnedChildNodes(container as ParentNode, previousNodes, childNodes);
|
|
1891
2694
|
fiber.memoizedState = childNodes;
|
|
1892
2695
|
} else {
|
|
1893
|
-
|
|
2696
|
+
commitHostDirtyChildrenOf(
|
|
2697
|
+
fiber,
|
|
1894
2698
|
fiber.child,
|
|
1895
2699
|
container as ParentNode,
|
|
1896
2700
|
portalEventRoot,
|
|
@@ -1904,8 +2708,14 @@ function commitHostDirtyFiber(
|
|
|
1904
2708
|
return;
|
|
1905
2709
|
}
|
|
1906
2710
|
|
|
2711
|
+
if (fiber.tag === "reactive-dom-block") {
|
|
2712
|
+
fiber.memoizedProps = fiber.pendingProps;
|
|
2713
|
+
finishCommittedFiber(fiber);
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
|
|
1907
2717
|
if (fiber.subtreeFlags !== NoFlags) {
|
|
1908
|
-
|
|
2718
|
+
commitHostDirtyChildrenOf(fiber, fiber.child, parent, eventRoot, path, options);
|
|
1909
2719
|
}
|
|
1910
2720
|
fiber.memoizedProps = fiber.pendingProps;
|
|
1911
2721
|
finishCommittedFiber(fiber);
|
|
@@ -2072,7 +2882,10 @@ function readAppendSuffixCommitHint(value: unknown): AppendSuffixCommitHint | un
|
|
|
2072
2882
|
}
|
|
2073
2883
|
|
|
2074
2884
|
function commitHostSingleRemoval(fiber: Fiber, parent: ParentNode): boolean {
|
|
2075
|
-
const removed =
|
|
2885
|
+
const removed =
|
|
2886
|
+
fiber.deletions?.length === 1
|
|
2887
|
+
? fiber.deletions[0]
|
|
2888
|
+
: getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
|
|
2076
2889
|
|
|
2077
2890
|
if (removed === undefined) {
|
|
2078
2891
|
return false;
|
|
@@ -2089,6 +2902,10 @@ function commitHostSingleRemoval(fiber: Fiber, parent: ParentNode): boolean {
|
|
|
2089
2902
|
removedAny = true;
|
|
2090
2903
|
}
|
|
2091
2904
|
|
|
2905
|
+
if (removedAny) {
|
|
2906
|
+
fiber.deletions = undefined;
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2092
2909
|
return removedAny;
|
|
2093
2910
|
}
|
|
2094
2911
|
|
|
@@ -2166,6 +2983,11 @@ function isSameFiberSlot(current: Fiber, next: Fiber): boolean {
|
|
|
2166
2983
|
}
|
|
2167
2984
|
|
|
2168
2985
|
function collectCommittedHostNodes(fiber: Fiber): Node[] {
|
|
2986
|
+
if (fiber.tag === "reactive-dom-block") {
|
|
2987
|
+
const node = getReactiveDomBlockNode(fiber.stateNode);
|
|
2988
|
+
return node === undefined ? [] : [node];
|
|
2989
|
+
}
|
|
2990
|
+
|
|
2169
2991
|
if (
|
|
2170
2992
|
(fiber.tag === "host-component" || fiber.tag === "host-text") &&
|
|
2171
2993
|
fiber.stateNode instanceof Node
|
|
@@ -2185,6 +3007,10 @@ function collectCommittedHostNodes(fiber: Fiber): Node[] {
|
|
|
2185
3007
|
}
|
|
2186
3008
|
|
|
2187
3009
|
function hasCommittedHostNode(fiber: Fiber): boolean {
|
|
3010
|
+
if (fiber.tag === "reactive-dom-block") {
|
|
3011
|
+
return getReactiveDomBlockNode(fiber.stateNode) !== undefined;
|
|
3012
|
+
}
|
|
3013
|
+
|
|
2188
3014
|
if (
|
|
2189
3015
|
(fiber.tag === "host-component" || fiber.tag === "host-text") &&
|
|
2190
3016
|
fiber.stateNode instanceof Node
|
|
@@ -2249,6 +3075,13 @@ function commitHostFiber(
|
|
|
2249
3075
|
return [text];
|
|
2250
3076
|
}
|
|
2251
3077
|
|
|
3078
|
+
if (fiber.tag === "reactive-dom-block") {
|
|
3079
|
+
const node = getReactiveDomBlockNode(fiber.stateNode);
|
|
3080
|
+
fiber.memoizedProps = fiber.pendingProps;
|
|
3081
|
+
finishCommittedFiber(fiber);
|
|
3082
|
+
return node === undefined ? [] : [node];
|
|
3083
|
+
}
|
|
3084
|
+
|
|
2252
3085
|
if (fiber.tag === "host-component") {
|
|
2253
3086
|
const element = fiber.stateNode;
|
|
2254
3087
|
|
|
@@ -2269,6 +3102,7 @@ function commitHostFiber(
|
|
|
2269
3102
|
return [element];
|
|
2270
3103
|
}
|
|
2271
3104
|
|
|
3105
|
+
const isDomElement = isDomHostElement(element);
|
|
2272
3106
|
const props = fiber.pendingProps as Record<string, unknown>;
|
|
2273
3107
|
const previousProps = fiber.memoizedProps as Record<string, unknown> | undefined;
|
|
2274
3108
|
const directTextChild =
|
|
@@ -2294,7 +3128,7 @@ function commitHostFiber(
|
|
|
2294
3128
|
fiber.hydrateExisting !== true &&
|
|
2295
3129
|
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
2296
3130
|
|
|
2297
|
-
if (
|
|
3131
|
+
if (isDomElement && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
2298
3132
|
applyProps(element, props, path, {
|
|
2299
3133
|
...options,
|
|
2300
3134
|
eventRoot,
|
|
@@ -2313,8 +3147,8 @@ function commitHostFiber(
|
|
|
2313
3147
|
) {
|
|
2314
3148
|
const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2315
3149
|
if (
|
|
2316
|
-
!(
|
|
2317
|
-
!(
|
|
3150
|
+
!(isDomElement && childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
3151
|
+
!(isDomElement && shouldPreserveContentEditableChildren(element, props, childNodes))
|
|
2318
3152
|
) {
|
|
2319
3153
|
syncChildNodes(element as ParentNode, childNodes);
|
|
2320
3154
|
}
|
|
@@ -2322,7 +3156,7 @@ function commitHostFiber(
|
|
|
2322
3156
|
commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2323
3157
|
}
|
|
2324
3158
|
|
|
2325
|
-
if (
|
|
3159
|
+
if (isDomElement && isFormHostType(fiber.type)) {
|
|
2326
3160
|
applyPostChildFormProps(element, props, previousProps);
|
|
2327
3161
|
}
|
|
2328
3162
|
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
@@ -2460,6 +3294,36 @@ function finishCommittedFiber(fiber: Fiber): void {
|
|
|
2460
3294
|
fiber.hostChildListChanged = false;
|
|
2461
3295
|
}
|
|
2462
3296
|
|
|
3297
|
+
function getReactiveDomBlockNode(state: unknown): ChildNode | undefined {
|
|
3298
|
+
if (
|
|
3299
|
+
typeof state === "object" &&
|
|
3300
|
+
state !== null &&
|
|
3301
|
+
"node" in state &&
|
|
3302
|
+
(state as { node?: unknown }).node instanceof Node
|
|
3303
|
+
) {
|
|
3304
|
+
return (state as { node: ChildNode }).node;
|
|
3305
|
+
}
|
|
3306
|
+
|
|
3307
|
+
return undefined;
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3310
|
+
function disposeReactiveDomBlockState(
|
|
3311
|
+
state: unknown,
|
|
3312
|
+
seen: Set<unknown>,
|
|
3313
|
+
): void {
|
|
3314
|
+
if (typeof state !== "object" || state === null || seen.has(state)) {
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
|
|
3318
|
+
seen.add(state);
|
|
3319
|
+
const dispose = (state as { dispose?: unknown }).dispose;
|
|
3320
|
+
|
|
3321
|
+
if (typeof dispose === "function") {
|
|
3322
|
+
dispose();
|
|
3323
|
+
(state as { dispose?: unknown }).dispose = undefined;
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
|
|
2463
3327
|
function hasChildListMutation(fiber: Fiber): boolean {
|
|
2464
3328
|
return fiber.childListChanged || fiber.subtreeChildListChanged;
|
|
2465
3329
|
}
|
|
@@ -2659,6 +3523,13 @@ function getDirectHostTextChild(children: unknown): string | undefined {
|
|
|
2659
3523
|
: undefined;
|
|
2660
3524
|
}
|
|
2661
3525
|
|
|
3526
|
+
// Only these host tags carry post-child form value/checked semantics. Gating on
|
|
3527
|
+
// the (cheap, already-known) type string lets the commit skip applyPostChildFormProps
|
|
3528
|
+
// and its instanceof probes for every other element.
|
|
3529
|
+
function isFormHostType(type: unknown): boolean {
|
|
3530
|
+
return type === "input" || type === "textarea" || type === "select";
|
|
3531
|
+
}
|
|
3532
|
+
|
|
2662
3533
|
// This package has no Node type dependency; declare the minimal process
|
|
2663
3534
|
// shape needed for the literal process.env.NODE_ENV expression below.
|
|
2664
3535
|
declare const process: { env: Record<string, string | undefined> };
|
|
@@ -3138,13 +4009,26 @@ function tryReuseMemoBailout(
|
|
|
3138
4009
|
return undefined;
|
|
3139
4010
|
}
|
|
3140
4011
|
|
|
3141
|
-
|
|
3142
|
-
runtime,
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
canReuseCurrentFiber
|
|
3147
|
-
|
|
4012
|
+
if (memoStateNeedsActiveInstanceMark(previousMemoState)) {
|
|
4013
|
+
markActiveInstanceKeys(runtime, previousMemoState.instanceKeys);
|
|
4014
|
+
}
|
|
4015
|
+
|
|
4016
|
+
const fiber =
|
|
4017
|
+
canReuseCurrentFiber &&
|
|
4018
|
+
current.hasRefSubtree !== true &&
|
|
4019
|
+
current.hasDisposableResources !== true &&
|
|
4020
|
+
current.hydrateExisting !== true
|
|
4021
|
+
? current
|
|
4022
|
+
: createWorkInProgress(current, node.props);
|
|
4023
|
+
|
|
4024
|
+
if (fiber === current) {
|
|
4025
|
+
current.pendingProps = node.props;
|
|
4026
|
+
current.flags = NoFlags;
|
|
4027
|
+
current.subtreeFlags = NoFlags;
|
|
4028
|
+
current.childListChanged = false;
|
|
4029
|
+
current.subtreeChildListChanged = false;
|
|
4030
|
+
current.hostChildListChanged = false;
|
|
4031
|
+
}
|
|
3148
4032
|
fiber.type = node.type;
|
|
3149
4033
|
fiber.child = getSkippedChild(current);
|
|
3150
4034
|
fiber.memoizedState = previousMemoState;
|
|
@@ -3511,6 +4395,35 @@ function collectInstanceKeys(runtime: RootRuntime, prefix: string): string[] {
|
|
|
3511
4395
|
return collectRuntimeInstanceKeys(runtime, prefix);
|
|
3512
4396
|
}
|
|
3513
4397
|
|
|
4398
|
+
function collectMemoInstanceKeys(runtime: RootRuntime, prefix: string): string[] {
|
|
4399
|
+
return readDependencyFreeMemoInstanceKey(runtime, prefix) === undefined
|
|
4400
|
+
? collectInstanceKeys(runtime, prefix)
|
|
4401
|
+
: emptyInstanceKeys;
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
function readDependencyFreeMemoInstanceKey(
|
|
4405
|
+
runtime: RootRuntime,
|
|
4406
|
+
prefix: string,
|
|
4407
|
+
): string | undefined {
|
|
4408
|
+
const keys = runtime.instanceKeysByPrefix.get(prefix);
|
|
4409
|
+
|
|
4410
|
+
if (keys === undefined || keys.size !== 1 || !keys.has(prefix)) {
|
|
4411
|
+
return undefined;
|
|
4412
|
+
}
|
|
4413
|
+
|
|
4414
|
+
const instance = runtime.instances.get(prefix) as RuntimeInstanceLike | undefined;
|
|
4415
|
+
|
|
4416
|
+
if (
|
|
4417
|
+
instance === undefined ||
|
|
4418
|
+
instance.contextDependencies !== undefined ||
|
|
4419
|
+
(instance.hooks !== undefined && instance.hooks.length > 0)
|
|
4420
|
+
) {
|
|
4421
|
+
return undefined;
|
|
4422
|
+
}
|
|
4423
|
+
|
|
4424
|
+
return prefix;
|
|
4425
|
+
}
|
|
4426
|
+
|
|
3514
4427
|
function markActiveInstanceKeys(runtime: RootRuntime, keys: readonly string[]): void {
|
|
3515
4428
|
for (const key of keys) {
|
|
3516
4429
|
runtime.activeInstanceKeys?.add(key);
|
|
@@ -3550,6 +4463,10 @@ function getMemoBailoutFiber(
|
|
|
3550
4463
|
state: MemoFiberState,
|
|
3551
4464
|
canReuseCurrentFiber: boolean,
|
|
3552
4465
|
): Fiber {
|
|
4466
|
+
if (memoStateNeedsActiveInstanceMark(state)) {
|
|
4467
|
+
markActiveInstanceKeys(runtime, state.instanceKeys);
|
|
4468
|
+
}
|
|
4469
|
+
|
|
3553
4470
|
if (canReuseCurrentFiber && canReuseMemoBailoutFiber(current, state)) {
|
|
3554
4471
|
current.pendingProps = pendingProps;
|
|
3555
4472
|
current.flags = NoFlags;
|
|
@@ -3561,9 +4478,6 @@ function getMemoBailoutFiber(
|
|
|
3561
4478
|
}
|
|
3562
4479
|
|
|
3563
4480
|
const fiber = createWorkInProgress(current, pendingProps);
|
|
3564
|
-
if (memoStateNeedsActiveInstanceMark(state)) {
|
|
3565
|
-
markActiveInstanceKeys(runtime, state.instanceKeys);
|
|
3566
|
-
}
|
|
3567
4481
|
return fiber;
|
|
3568
4482
|
}
|
|
3569
4483
|
|
|
@@ -3571,6 +4485,7 @@ function canReuseMemoBailoutFiber(current: Fiber, state: MemoFiberState): boolea
|
|
|
3571
4485
|
return (
|
|
3572
4486
|
state.hasRetainedInstanceDependencies === false &&
|
|
3573
4487
|
current.hasRefSubtree !== true &&
|
|
4488
|
+
current.hasDisposableResources !== true &&
|
|
3574
4489
|
current.hydrateExisting !== true
|
|
3575
4490
|
);
|
|
3576
4491
|
}
|