@reckona/mreact-compat 0.0.163 → 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/dist/dom-children.d.ts +1 -0
- package/dist/dom-children.d.ts.map +1 -1
- package/dist/dom-children.js +10 -1
- package/dist/dom-children.js.map +1 -1
- package/dist/dom-host-rules.d.ts +15 -1
- package/dist/dom-host-rules.d.ts.map +1 -1
- package/dist/dom-host-rules.js +17 -1
- package/dist/dom-host-rules.js.map +1 -1
- package/dist/dom-props.d.ts.map +1 -1
- package/dist/dom-props.js +8 -0
- package/dist/dom-props.js.map +1 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +20 -2
- package/dist/events.js.map +1 -1
- package/dist/fiber-child.js +11 -5
- package/dist/fiber-child.js.map +1 -1
- package/dist/fiber-commit.d.ts.map +1 -1
- package/dist/fiber-commit.js +51 -31
- package/dist/fiber-commit.js.map +1 -1
- package/dist/hooks.d.ts +2 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +57 -15
- package/dist/hooks.js.map +1 -1
- package/dist/host-reconciler.d.ts +2 -2
- package/dist/host-reconciler.d.ts.map +1 -1
- package/dist/host-reconciler.js +196 -43
- package/dist/host-reconciler.js.map +1 -1
- package/dist/root.js +3 -2
- package/dist/root.js.map +1 -1
- package/package.json +3 -3
- package/src/dom-children.ts +16 -1
- package/src/dom-host-rules.ts +42 -3
- package/src/dom-props.ts +14 -0
- package/src/events.ts +27 -2
- package/src/fiber-child.ts +12 -5
- package/src/fiber-commit.ts +61 -37
- package/src/hooks.ts +61 -16
- package/src/host-reconciler.ts +267 -53
- package/src/root.ts +3 -2
package/src/host-reconciler.ts
CHANGED
|
@@ -27,13 +27,15 @@ import {
|
|
|
27
27
|
import { applyPostChildFormProps, applyProps } from "./dom-props.js";
|
|
28
28
|
import { syncChildNodes, syncOwnedChildNodes, syncScopedChildNodes } from "./dom-children.js";
|
|
29
29
|
import { setLogicalEventParent } from "./host-event-binder.js";
|
|
30
|
-
import { NoFlags, Placement, Update } from "./fiber-flags.js";
|
|
30
|
+
import { ChildDeletion, NoFlags, Placement, Update } from "./fiber-flags.js";
|
|
31
31
|
import {
|
|
32
32
|
createHostElement,
|
|
33
33
|
hostElementMatches,
|
|
34
|
+
isDomHostElement,
|
|
34
35
|
isHostElement,
|
|
35
36
|
namespaceForHostChildren,
|
|
36
37
|
namespaceForHostElement,
|
|
38
|
+
type CustomHostDocument,
|
|
37
39
|
type HostNamespace,
|
|
38
40
|
} from "./dom-host-rules.js";
|
|
39
41
|
import { createFiber, createWorkInProgress, type Fiber, type FiberRoot } from "./fiber.js";
|
|
@@ -90,13 +92,14 @@ interface SuspenseFiberState {
|
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
const committedPortalContainers = new Set<Element>();
|
|
95
|
+
const pendingHostRefUpdates: { ref: unknown; node: unknown }[] = [];
|
|
93
96
|
|
|
94
97
|
interface FiberHydrationOptions extends RenderOptions {
|
|
95
98
|
previousNodes?: readonly Node[];
|
|
96
99
|
resumeId?: string;
|
|
97
100
|
consumeResumeMarkers?: boolean;
|
|
98
101
|
namespace?: HostNamespace;
|
|
99
|
-
documentRef?: Document;
|
|
102
|
+
documentRef?: Document | CustomHostDocument;
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
const SKIP_COMMIT_PATH = "\0";
|
|
@@ -107,6 +110,11 @@ interface FiberReconcileResult {
|
|
|
107
110
|
consumed: number;
|
|
108
111
|
}
|
|
109
112
|
|
|
113
|
+
interface AppendSuffixCommitHint {
|
|
114
|
+
fiber: Fiber;
|
|
115
|
+
index: number;
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
interface ReactSuspenseBoundary {
|
|
111
119
|
previousNodes?: Node[];
|
|
112
120
|
consumed: number;
|
|
@@ -241,11 +249,14 @@ export function commitHostFiberRoot(
|
|
|
241
249
|
options: RenderOptions = {},
|
|
242
250
|
): void {
|
|
243
251
|
runWithHostCommit(() => {
|
|
252
|
+
let committed = false;
|
|
244
253
|
try {
|
|
245
254
|
committedPortalContainers.clear();
|
|
255
|
+
pendingHostRefUpdates.length = 0;
|
|
246
256
|
const commitPath = getRootCommitPath(options);
|
|
247
257
|
if (!hasChildListMutation(finishedWork)) {
|
|
248
258
|
commitHostDirtyChildren(finishedWork.child, root.container, root.container, commitPath, options);
|
|
259
|
+
committed = true;
|
|
249
260
|
return;
|
|
250
261
|
}
|
|
251
262
|
|
|
@@ -254,12 +265,19 @@ export function commitHostFiberRoot(
|
|
|
254
265
|
finishedWork.subtreeChildListChanged &&
|
|
255
266
|
commitHostKeyedChildListMutation(finishedWork.child, root.container, root.container, commitPath, options)
|
|
256
267
|
) {
|
|
268
|
+
committed = true;
|
|
257
269
|
return;
|
|
258
270
|
}
|
|
259
271
|
|
|
260
272
|
const nodes = commitHostChildren(finishedWork.child, root.container, root.container, commitPath, options);
|
|
261
273
|
syncChildNodes(root.container, nodes);
|
|
274
|
+
committed = true;
|
|
262
275
|
} finally {
|
|
276
|
+
if (committed) {
|
|
277
|
+
flushPendingHostRefUpdates();
|
|
278
|
+
} else {
|
|
279
|
+
pendingHostRefUpdates.length = 0;
|
|
280
|
+
}
|
|
263
281
|
committedPortalContainers.clear();
|
|
264
282
|
}
|
|
265
283
|
});
|
|
@@ -272,12 +290,20 @@ export function commitHydratingHostFiberRoot(
|
|
|
272
290
|
options: FiberHydrationOptions = {},
|
|
273
291
|
): void {
|
|
274
292
|
runWithHostCommit(() => {
|
|
293
|
+
let committed = false;
|
|
275
294
|
try {
|
|
276
295
|
committedPortalContainers.clear();
|
|
296
|
+
pendingHostRefUpdates.length = 0;
|
|
277
297
|
const eventRoot = root.container;
|
|
278
298
|
const nodes = commitHostChildren(finishedWork.child, scope.parent, eventRoot, "", options);
|
|
279
299
|
syncScopedChildNodes(scope.parent, scope.before, scope.after, nodes);
|
|
300
|
+
committed = true;
|
|
280
301
|
} finally {
|
|
302
|
+
if (committed) {
|
|
303
|
+
flushPendingHostRefUpdates();
|
|
304
|
+
} else {
|
|
305
|
+
pendingHostRefUpdates.length = 0;
|
|
306
|
+
}
|
|
281
307
|
committedPortalContainers.clear();
|
|
282
308
|
}
|
|
283
309
|
});
|
|
@@ -303,6 +329,9 @@ function reconcileHostChild(
|
|
|
303
329
|
|
|
304
330
|
if (node === null || node === undefined || typeof node === "boolean") {
|
|
305
331
|
parent.childListChanged = currentFirstChild !== undefined;
|
|
332
|
+
if (currentFirstChild !== undefined) {
|
|
333
|
+
markOptimizedChildrenForDeletion(parent, currentFirstChild);
|
|
334
|
+
}
|
|
306
335
|
return { fiber: undefined, consumed: 0 };
|
|
307
336
|
}
|
|
308
337
|
|
|
@@ -324,6 +353,8 @@ function reconcileHostChild(
|
|
|
324
353
|
let previous: Fiber | undefined;
|
|
325
354
|
let consumed = 0;
|
|
326
355
|
let skipRemainingKeyedLookup = false;
|
|
356
|
+
const usedCurrentChildren =
|
|
357
|
+
currentFirstChild === undefined ? undefined : new Set<Fiber>();
|
|
327
358
|
|
|
328
359
|
for (let index = 0; index < childCount; index += 1) {
|
|
329
360
|
const child = children === undefined ? node : children[index];
|
|
@@ -376,9 +407,20 @@ function reconcileHostChild(
|
|
|
376
407
|
const fiber = result.fiber;
|
|
377
408
|
|
|
378
409
|
if (fiber === undefined) {
|
|
410
|
+
if (matchedCurrent !== undefined) {
|
|
411
|
+
usedCurrentChildren?.add(matchedCurrent);
|
|
412
|
+
markOptimizedChildForDeletion(parent, matchedCurrent);
|
|
413
|
+
}
|
|
379
414
|
continue;
|
|
380
415
|
}
|
|
381
416
|
|
|
417
|
+
if (matchedCurrent !== undefined) {
|
|
418
|
+
usedCurrentChildren?.add(matchedCurrent);
|
|
419
|
+
if (fiber !== matchedCurrent && fiber.alternate !== matchedCurrent) {
|
|
420
|
+
markOptimizedChildForDeletion(parent, matchedCurrent);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
382
424
|
if (key === undefined) {
|
|
383
425
|
currentUnkeyed = currentUnkeyed?.sibling;
|
|
384
426
|
}
|
|
@@ -407,6 +449,7 @@ function reconcileHostChild(
|
|
|
407
449
|
previous = fiber;
|
|
408
450
|
}
|
|
409
451
|
|
|
452
|
+
markUnusedCurrentChildrenForDeletion(parent, currentFirstChild, usedCurrentChildren);
|
|
410
453
|
parent.childListChanged = childFiberListShapeChanged(currentFirstChild, first);
|
|
411
454
|
|
|
412
455
|
return { fiber: first, consumed };
|
|
@@ -435,10 +478,8 @@ function reconcileKeyedRowHostChildren(
|
|
|
435
478
|
let subtreeFlags = NoFlags;
|
|
436
479
|
let subtreeChildListChanged = false;
|
|
437
480
|
let hasRefSubtree = false;
|
|
438
|
-
|
|
439
|
-
const canReuseUnchangedRows =
|
|
440
|
-
currentSiblingCount === children.length ||
|
|
441
|
-
isKeyedAppendOnly(currentFirstChild, children, currentSiblingCount);
|
|
481
|
+
let appendSuffix: AppendSuffixCommitHint | undefined;
|
|
482
|
+
const canReuseUnchangedRows = hasSameKeyOrderPrefix(currentFirstChild, children);
|
|
442
483
|
const row = createKeyedRowHostElementScratch();
|
|
443
484
|
|
|
444
485
|
for (let index = 0; index < children.length; index += 1) {
|
|
@@ -449,12 +490,14 @@ function reconcileKeyedRowHostChildren(
|
|
|
449
490
|
}
|
|
450
491
|
|
|
451
492
|
let matchedCurrent: Fiber | undefined;
|
|
493
|
+
let matchedByAppendSuffix = false;
|
|
452
494
|
|
|
453
495
|
if (skipRemainingKeyedLookup) {
|
|
454
496
|
matchedCurrent = undefined;
|
|
455
497
|
} else if (currentKeyed === undefined) {
|
|
456
498
|
listShapeChanged = true;
|
|
457
499
|
skipRemainingKeyedLookup = true;
|
|
500
|
+
matchedByAppendSuffix = true;
|
|
458
501
|
matchedCurrent = undefined;
|
|
459
502
|
} else if (currentKeyed?.key === row.key) {
|
|
460
503
|
matchedCurrent = currentKeyed;
|
|
@@ -463,11 +506,15 @@ function reconcileKeyedRowHostChildren(
|
|
|
463
506
|
currentKeyed?.sibling?.key === row.key &&
|
|
464
507
|
canSkipSingleDeletedKeyedFiber(children, index, currentKeyed.sibling)
|
|
465
508
|
) {
|
|
509
|
+
const deleted = currentKeyed;
|
|
510
|
+
const matched = currentKeyed.sibling;
|
|
466
511
|
listShapeChanged = true;
|
|
467
|
-
|
|
468
|
-
|
|
512
|
+
markOptimizedChildForDeletion(parent, deleted);
|
|
513
|
+
matchedCurrent = matched;
|
|
514
|
+
currentKeyed = matched.sibling;
|
|
469
515
|
} else if (canSkipRemainingKeyedLookup(currentKeyed, children, index)) {
|
|
470
516
|
listShapeChanged = true;
|
|
517
|
+
markOptimizedChildrenForDeletion(parent, currentKeyed);
|
|
471
518
|
skipRemainingKeyedLookup = true;
|
|
472
519
|
currentKeyed = undefined;
|
|
473
520
|
matchedCurrent = undefined;
|
|
@@ -481,6 +528,10 @@ function reconcileKeyedRowHostChildren(
|
|
|
481
528
|
: (canReuseUnchangedRows ? getReusableKeyedRowHostFiber(matchedCurrent, row) : undefined) ??
|
|
482
529
|
createKeyedRowHostFiber(parent, matchedCurrent, row, options);
|
|
483
530
|
|
|
531
|
+
if (matchedByAppendSuffix && appendSuffix === undefined) {
|
|
532
|
+
appendSuffix = { fiber, index };
|
|
533
|
+
}
|
|
534
|
+
|
|
484
535
|
if (first === undefined) {
|
|
485
536
|
first = fiber;
|
|
486
537
|
} else if (previous !== undefined) {
|
|
@@ -505,42 +556,70 @@ function reconcileKeyedRowHostChildren(
|
|
|
505
556
|
|
|
506
557
|
if (currentKeyed !== undefined) {
|
|
507
558
|
listShapeChanged = true;
|
|
559
|
+
markOptimizedChildrenForDeletion(parent, currentKeyed);
|
|
508
560
|
}
|
|
509
561
|
|
|
510
562
|
parent.hasRefSubtree = hasRefSubtree;
|
|
511
563
|
parent.subtreeFlags = subtreeFlags;
|
|
512
564
|
parent.subtreeChildListChanged = subtreeChildListChanged;
|
|
513
565
|
parent.childListChanged = listShapeChanged;
|
|
566
|
+
if (appendSuffix !== undefined && canStoreAppendSuffixCommitHint(parent)) {
|
|
567
|
+
parent.memoizedState = appendSuffix;
|
|
568
|
+
}
|
|
514
569
|
return { fiber: first, consumed: 0 };
|
|
515
570
|
}
|
|
516
571
|
|
|
517
|
-
function
|
|
518
|
-
|
|
519
|
-
|
|
572
|
+
function canStoreAppendSuffixCommitHint(parent: Fiber): boolean {
|
|
573
|
+
return (
|
|
574
|
+
parent.tag === "fragment" ||
|
|
575
|
+
parent.tag === "host-component" ||
|
|
576
|
+
parent.tag === "host-root"
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
function markOptimizedChildForDeletion(parent: Fiber, _child: Fiber): void {
|
|
581
|
+
parent.flags |= ChildDeletion;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function markOptimizedChildrenForDeletion(parent: Fiber, _firstChild: Fiber): void {
|
|
585
|
+
parent.flags |= ChildDeletion;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function markUnusedCurrentChildrenForDeletion(
|
|
589
|
+
parent: Fiber,
|
|
590
|
+
firstChild: Fiber | undefined,
|
|
591
|
+
used: ReadonlySet<Fiber> | undefined,
|
|
592
|
+
): void {
|
|
593
|
+
if (firstChild === undefined || used === undefined) {
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
let cursor: Fiber | undefined = firstChild;
|
|
520
598
|
|
|
521
599
|
while (cursor !== undefined) {
|
|
522
|
-
|
|
600
|
+
if (!used.has(cursor)) {
|
|
601
|
+
parent.flags |= ChildDeletion;
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
523
604
|
cursor = cursor.sibling;
|
|
524
605
|
}
|
|
525
|
-
|
|
526
|
-
return count;
|
|
527
606
|
}
|
|
528
607
|
|
|
529
|
-
function
|
|
608
|
+
function hasSameKeyOrderPrefix(
|
|
530
609
|
currentFirstChild: Fiber,
|
|
531
610
|
children: readonly ReactCompatNode[],
|
|
532
|
-
currentSiblingCount: number,
|
|
533
611
|
): boolean {
|
|
534
|
-
if (children.length <= currentSiblingCount) {
|
|
535
|
-
return false;
|
|
536
|
-
}
|
|
537
|
-
|
|
538
612
|
let current: Fiber | undefined = currentFirstChild;
|
|
539
613
|
|
|
540
|
-
for (let index = 0; index <
|
|
541
|
-
if (current === undefined
|
|
614
|
+
for (let index = 0; index < children.length; index += 1) {
|
|
615
|
+
if (current === undefined) {
|
|
616
|
+
return true;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (current.key !== getNodeKey(children[index])) {
|
|
542
620
|
return false;
|
|
543
621
|
}
|
|
622
|
+
|
|
544
623
|
current = current.sibling;
|
|
545
624
|
}
|
|
546
625
|
|
|
@@ -942,7 +1021,7 @@ function createHostFiberImpl(
|
|
|
942
1021
|
? existing
|
|
943
1022
|
: current?.tag === "host-text" && current.stateNode instanceof Text
|
|
944
1023
|
? current.stateNode
|
|
945
|
-
: getDocumentRef(options)
|
|
1024
|
+
: createHostTextNode(getDocumentRef(options));
|
|
946
1025
|
|
|
947
1026
|
if (existing instanceof Text && existing.data !== String(node)) {
|
|
948
1027
|
reportRecoverable(
|
|
@@ -1642,13 +1721,12 @@ function commitHostDirtyFiber(
|
|
|
1642
1721
|
fiber.hydrateExisting !== true &&
|
|
1643
1722
|
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
1644
1723
|
|
|
1645
|
-
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
1724
|
+
if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
1646
1725
|
applyProps(element, props, path, {
|
|
1647
1726
|
...options,
|
|
1648
1727
|
eventRoot,
|
|
1649
1728
|
preserveHydrationAttributes: fiber.hydrateExisting,
|
|
1650
1729
|
});
|
|
1651
|
-
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
1652
1730
|
}
|
|
1653
1731
|
|
|
1654
1732
|
if (directTextChild !== undefined) {
|
|
@@ -1661,16 +1739,19 @@ function commitHostDirtyFiber(
|
|
|
1661
1739
|
) {
|
|
1662
1740
|
const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1663
1741
|
if (
|
|
1664
|
-
!(childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
1665
|
-
!shouldPreserveContentEditableChildren(element, props, childNodes)
|
|
1742
|
+
!(isDomHostElement(element) && childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
1743
|
+
!(isDomHostElement(element) && shouldPreserveContentEditableChildren(element, props, childNodes))
|
|
1666
1744
|
) {
|
|
1667
|
-
syncChildNodes(element, childNodes);
|
|
1745
|
+
syncChildNodes(element as ParentNode, childNodes);
|
|
1668
1746
|
}
|
|
1669
1747
|
} else if (fiber.subtreeFlags !== NoFlags) {
|
|
1670
1748
|
commitHostDirtyChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
1671
1749
|
}
|
|
1672
1750
|
|
|
1673
|
-
|
|
1751
|
+
if (isDomHostElement(element)) {
|
|
1752
|
+
applyPostChildFormProps(element, props, previousProps);
|
|
1753
|
+
}
|
|
1754
|
+
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
1674
1755
|
fiber.memoizedProps = props;
|
|
1675
1756
|
finishCommittedFiber(fiber);
|
|
1676
1757
|
return;
|
|
@@ -1679,9 +1760,42 @@ function commitHostDirtyFiber(
|
|
|
1679
1760
|
if (fiber.tag === "portal") {
|
|
1680
1761
|
const container = fiber.stateNode;
|
|
1681
1762
|
|
|
1682
|
-
if (container
|
|
1683
|
-
|
|
1684
|
-
|
|
1763
|
+
if (isPortalHostContainer(container)) {
|
|
1764
|
+
if (container instanceof Element) {
|
|
1765
|
+
setLogicalEventParent(container, parent);
|
|
1766
|
+
}
|
|
1767
|
+
const portalEventRoot =
|
|
1768
|
+
container instanceof Element && eventRoot !== container && eventRoot.contains(container)
|
|
1769
|
+
? eventRoot
|
|
1770
|
+
: container instanceof Element
|
|
1771
|
+
? container
|
|
1772
|
+
: eventRoot;
|
|
1773
|
+
const portalOptions = withPortalDocumentRef(options, container);
|
|
1774
|
+
|
|
1775
|
+
if (
|
|
1776
|
+
fiber.childListChanged ||
|
|
1777
|
+
fiber.subtreeChildListChanged ||
|
|
1778
|
+
(fiber.subtreeFlags & Placement) !== NoFlags
|
|
1779
|
+
) {
|
|
1780
|
+
const childNodes = commitHostChildren(
|
|
1781
|
+
fiber.child,
|
|
1782
|
+
container as ParentNode,
|
|
1783
|
+
portalEventRoot,
|
|
1784
|
+
`${path}.portal`,
|
|
1785
|
+
portalOptions,
|
|
1786
|
+
);
|
|
1787
|
+
const previousNodes = committedHostNodesFromState(fiber.alternate?.memoizedState);
|
|
1788
|
+
syncOwnedChildNodes(container as ParentNode, previousNodes, childNodes);
|
|
1789
|
+
fiber.memoizedState = childNodes;
|
|
1790
|
+
} else {
|
|
1791
|
+
commitHostDirtyChildren(
|
|
1792
|
+
fiber.child,
|
|
1793
|
+
container as ParentNode,
|
|
1794
|
+
portalEventRoot,
|
|
1795
|
+
`${path}.portal`,
|
|
1796
|
+
portalOptions,
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1685
1799
|
}
|
|
1686
1800
|
fiber.memoizedProps = fiber.pendingProps;
|
|
1687
1801
|
finishCommittedFiber(fiber);
|
|
@@ -1752,6 +1866,11 @@ function commitHostKeyedChildListMutationFiber(
|
|
|
1752
1866
|
path: string,
|
|
1753
1867
|
options: RenderOptions = {},
|
|
1754
1868
|
): boolean {
|
|
1869
|
+
if (fiber.tag === "portal") {
|
|
1870
|
+
commitHostDirtyFiber(fiber, parent, eventRoot, path, options);
|
|
1871
|
+
return true;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1755
1874
|
if (fiber.childListChanged) {
|
|
1756
1875
|
const mutationParent =
|
|
1757
1876
|
fiber.tag === "host-component" && isHostElement(fiber.stateNode)
|
|
@@ -1808,17 +1927,28 @@ function commitHostAppendSuffix(
|
|
|
1808
1927
|
path: string,
|
|
1809
1928
|
options: RenderOptions,
|
|
1810
1929
|
): boolean {
|
|
1811
|
-
const
|
|
1930
|
+
const appendHint = readAppendSuffixCommitHint(fiber.memoizedState);
|
|
1931
|
+
const append = appendHint ?? getAppendSuffix(fiber.alternate?.child, fiber.child);
|
|
1812
1932
|
|
|
1813
1933
|
if (append === undefined) {
|
|
1814
1934
|
return false;
|
|
1815
1935
|
}
|
|
1816
1936
|
|
|
1937
|
+
if (appendHint !== undefined) {
|
|
1938
|
+
fiber.memoizedState = undefined;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1817
1941
|
let cursor: Fiber | undefined = append.fiber;
|
|
1818
1942
|
let index = append.index;
|
|
1819
1943
|
|
|
1820
1944
|
while (cursor !== undefined) {
|
|
1821
|
-
for (const node of commitHostFiber(
|
|
1945
|
+
for (const node of commitHostFiber(
|
|
1946
|
+
cursor,
|
|
1947
|
+
parent,
|
|
1948
|
+
eventRoot,
|
|
1949
|
+
joinCommitPath(path, String(index)),
|
|
1950
|
+
options,
|
|
1951
|
+
)) {
|
|
1822
1952
|
parent.appendChild(node);
|
|
1823
1953
|
}
|
|
1824
1954
|
cursor = cursor.sibling;
|
|
@@ -1828,6 +1958,17 @@ function commitHostAppendSuffix(
|
|
|
1828
1958
|
return true;
|
|
1829
1959
|
}
|
|
1830
1960
|
|
|
1961
|
+
function readAppendSuffixCommitHint(value: unknown): AppendSuffixCommitHint | undefined {
|
|
1962
|
+
if (typeof value !== "object" || value === null) {
|
|
1963
|
+
return undefined;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
const candidate = value as Partial<AppendSuffixCommitHint>;
|
|
1967
|
+
return candidate.fiber !== undefined && typeof candidate.index === "number"
|
|
1968
|
+
? { fiber: candidate.fiber, index: candidate.index }
|
|
1969
|
+
: undefined;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1831
1972
|
function commitHostSingleRemoval(fiber: Fiber, parent: ParentNode): boolean {
|
|
1832
1973
|
const removed = getSingleRemovedFiber(fiber.alternate?.child, fiber.child);
|
|
1833
1974
|
|
|
@@ -2031,13 +2172,12 @@ function commitHostFiber(
|
|
|
2031
2172
|
fiber.hydrateExisting !== true &&
|
|
2032
2173
|
isRowTextOnlyUpdate(fiber.memoizedProps, props);
|
|
2033
2174
|
|
|
2034
|
-
if (!propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
2175
|
+
if (isDomHostElement(element) && !propsAreUnchanged && !propsAreChildrenOnly && !textOnlyRowUpdate) {
|
|
2035
2176
|
applyProps(element, props, path, {
|
|
2036
2177
|
...options,
|
|
2037
2178
|
eventRoot,
|
|
2038
2179
|
preserveHydrationAttributes: fiber.hydrateExisting,
|
|
2039
2180
|
});
|
|
2040
|
-
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
2041
2181
|
}
|
|
2042
2182
|
if (directTextChild !== undefined) {
|
|
2043
2183
|
const text = syncDirectHostTextChild(element, directTextChild);
|
|
@@ -2051,16 +2191,19 @@ function commitHostFiber(
|
|
|
2051
2191
|
) {
|
|
2052
2192
|
const childNodes = commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2053
2193
|
if (
|
|
2054
|
-
!(childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
2055
|
-
!shouldPreserveContentEditableChildren(element, props, childNodes)
|
|
2194
|
+
!(isDomHostElement(element) && childNodes.length === 0 && committedPortalContainers.has(element)) &&
|
|
2195
|
+
!(isDomHostElement(element) && shouldPreserveContentEditableChildren(element, props, childNodes))
|
|
2056
2196
|
) {
|
|
2057
|
-
syncChildNodes(element, childNodes);
|
|
2197
|
+
syncChildNodes(element as ParentNode, childNodes);
|
|
2058
2198
|
}
|
|
2059
2199
|
} else if (fiber.subtreeFlags !== NoFlags) {
|
|
2060
2200
|
commitHostChildren(fiber.child, element, eventRoot, `${path}.c`, options);
|
|
2061
2201
|
}
|
|
2062
2202
|
|
|
2063
|
-
|
|
2203
|
+
if (isDomHostElement(element)) {
|
|
2204
|
+
applyPostChildFormProps(element, props, previousProps);
|
|
2205
|
+
}
|
|
2206
|
+
applyChangedRef(previousProps?.ref, props.ref, element);
|
|
2064
2207
|
fiber.memoizedProps = props;
|
|
2065
2208
|
finishCommittedFiber(fiber);
|
|
2066
2209
|
return [element];
|
|
@@ -2153,25 +2296,30 @@ function commitHostFiber(
|
|
|
2153
2296
|
if (fiber.tag === "portal") {
|
|
2154
2297
|
const container = fiber.stateNode;
|
|
2155
2298
|
|
|
2156
|
-
if (!(container
|
|
2299
|
+
if (!isPortalHostContainer(container)) {
|
|
2157
2300
|
return [];
|
|
2158
2301
|
}
|
|
2159
2302
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2303
|
+
if (container instanceof Element) {
|
|
2304
|
+
setLogicalEventParent(container, parent);
|
|
2305
|
+
committedPortalContainers.add(container);
|
|
2306
|
+
}
|
|
2162
2307
|
const portalEventRoot =
|
|
2163
|
-
eventRoot !== container && eventRoot.contains(container)
|
|
2308
|
+
container instanceof Element && eventRoot !== container && eventRoot.contains(container)
|
|
2309
|
+
? eventRoot
|
|
2310
|
+
: container instanceof Element
|
|
2311
|
+
? container
|
|
2312
|
+
: eventRoot;
|
|
2313
|
+
const portalOptions = withPortalDocumentRef(options, container);
|
|
2164
2314
|
const childNodes = commitHostChildren(
|
|
2165
2315
|
fiber.child,
|
|
2166
|
-
container,
|
|
2316
|
+
container as ParentNode,
|
|
2167
2317
|
portalEventRoot,
|
|
2168
2318
|
`${path}.portal`,
|
|
2169
|
-
|
|
2319
|
+
portalOptions,
|
|
2170
2320
|
);
|
|
2171
|
-
const previousNodes =
|
|
2172
|
-
|
|
2173
|
-
: [];
|
|
2174
|
-
syncOwnedChildNodes(container, previousNodes, childNodes);
|
|
2321
|
+
const previousNodes = committedHostNodesFromState(fiber.alternate?.memoizedState);
|
|
2322
|
+
syncOwnedChildNodes(container as ParentNode, previousNodes, childNodes);
|
|
2175
2323
|
fiber.memoizedState = childNodes;
|
|
2176
2324
|
fiber.memoizedProps = fiber.pendingProps;
|
|
2177
2325
|
finishCommittedFiber(fiber);
|
|
@@ -2983,10 +3131,18 @@ function normalizeChildren(node: ReactCompatNode): ReactCompatNode[] {
|
|
|
2983
3131
|
return Array.isArray(node) ? node : [node];
|
|
2984
3132
|
}
|
|
2985
3133
|
|
|
2986
|
-
function getDocumentRef(options: FiberHydrationOptions): Document {
|
|
3134
|
+
function getDocumentRef(options: FiberHydrationOptions): Document | CustomHostDocument {
|
|
2987
3135
|
return options.documentRef ?? document;
|
|
2988
3136
|
}
|
|
2989
3137
|
|
|
3138
|
+
function createHostTextNode(documentRef: Document | CustomHostDocument): Text {
|
|
3139
|
+
if ("createTextNode" in documentRef && typeof documentRef.createTextNode === "function") {
|
|
3140
|
+
return documentRef.createTextNode("");
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
return document.createTextNode("");
|
|
3144
|
+
}
|
|
3145
|
+
|
|
2990
3146
|
function collectExistingKeyedFibers(
|
|
2991
3147
|
firstChild: Fiber | undefined,
|
|
2992
3148
|
): Map<string, Fiber> {
|
|
@@ -3199,6 +3355,64 @@ function applyChangedRef(previousRef: unknown, nextRef: unknown, node: unknown):
|
|
|
3199
3355
|
return;
|
|
3200
3356
|
}
|
|
3201
3357
|
|
|
3202
|
-
|
|
3203
|
-
|
|
3358
|
+
queueHostRefUpdate(previousRef, null);
|
|
3359
|
+
queueHostRefUpdate(nextRef, node);
|
|
3360
|
+
}
|
|
3361
|
+
|
|
3362
|
+
function queueHostRefUpdate(ref: unknown, node: unknown): void {
|
|
3363
|
+
if (ref === null || ref === undefined) {
|
|
3364
|
+
return;
|
|
3365
|
+
}
|
|
3366
|
+
|
|
3367
|
+
pendingHostRefUpdates.push({ ref, node });
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
function flushPendingHostRefUpdates(): void {
|
|
3371
|
+
const pending = pendingHostRefUpdates.splice(0);
|
|
3372
|
+
for (const { ref, node } of pending) {
|
|
3373
|
+
applyRef(ref, node);
|
|
3374
|
+
}
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3377
|
+
function isPortalHostContainer(value: unknown): value is ParentNode {
|
|
3378
|
+
if (value instanceof Element) {
|
|
3379
|
+
return true;
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
if (typeof value !== "object" || value === null) {
|
|
3383
|
+
return false;
|
|
3384
|
+
}
|
|
3385
|
+
|
|
3386
|
+
const candidate = value as Partial<ParentNode> & {
|
|
3387
|
+
ownerDocument?: { createElement?: unknown };
|
|
3388
|
+
};
|
|
3389
|
+
return (
|
|
3390
|
+
typeof candidate.appendChild === "function" &&
|
|
3391
|
+
typeof candidate.insertBefore === "function" &&
|
|
3392
|
+
typeof candidate.removeChild === "function" &&
|
|
3393
|
+
typeof candidate.ownerDocument?.createElement === "function"
|
|
3394
|
+
);
|
|
3395
|
+
}
|
|
3396
|
+
|
|
3397
|
+
function withPortalDocumentRef(
|
|
3398
|
+
options: RenderOptions,
|
|
3399
|
+
container: ParentNode,
|
|
3400
|
+
): RenderOptions & { documentRef?: Document | CustomHostDocument } {
|
|
3401
|
+
const ownerDocument = (container as { ownerDocument?: unknown }).ownerDocument;
|
|
3402
|
+
if (
|
|
3403
|
+
typeof ownerDocument === "object" &&
|
|
3404
|
+
ownerDocument !== null &&
|
|
3405
|
+
typeof (ownerDocument as { createElement?: unknown }).createElement === "function"
|
|
3406
|
+
) {
|
|
3407
|
+
return {
|
|
3408
|
+
...options,
|
|
3409
|
+
documentRef: ownerDocument as Document | CustomHostDocument,
|
|
3410
|
+
};
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
return options;
|
|
3414
|
+
}
|
|
3415
|
+
|
|
3416
|
+
function committedHostNodesFromState(state: unknown): Node[] {
|
|
3417
|
+
return Array.isArray(state) ? state as Node[] : [];
|
|
3204
3418
|
}
|
package/src/root.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type RenderPriority,
|
|
7
7
|
type RootRuntime,
|
|
8
8
|
} from "./hooks.js";
|
|
9
|
-
import { removeChildIfPresent } from "./dom-children.js";
|
|
9
|
+
import { collectOwnedChildNodes, removeChildIfPresent } from "./dom-children.js";
|
|
10
10
|
import { commitDevToolsRoot, unmountDevToolsRoot } from "./devtools.js";
|
|
11
11
|
import {
|
|
12
12
|
applyStreamingHydrationFragments,
|
|
@@ -532,7 +532,8 @@ function removeStalePortalNodes(
|
|
|
532
532
|
snapshot: PortalRenderSnapshot,
|
|
533
533
|
runtime: RootRuntime,
|
|
534
534
|
): void {
|
|
535
|
-
for (const
|
|
535
|
+
for (const container of snapshot.containers) {
|
|
536
|
+
const nodes = snapshot.nodes.get(container) ?? new Set(collectOwnedChildNodes(container));
|
|
536
537
|
const currentNodes = runtime.portalNodes.get(container);
|
|
537
538
|
|
|
538
539
|
for (const node of nodes) {
|