8
import {HostComponent, HostText} from 'shared/ReactWorkTags';
9
import invariant from 'shared/invariant';
10
11
+import {getParentSuspenseInstance} from './ReactDOMHostConfig';
12
+
13
const randomKey = Math.random()
14
.toString(36)
15
.slice(2);
16
const internalInstanceKey = '__reactInternalInstance$' + randomKey;
17
const internalEventHandlersKey = '__reactEventHandlers$' + randomKey;
18
+const internalContainerInstanceKey = '__reactContainere$' + randomKey;
19
20
export function precacheFiberNode(hostInst, node) {
21
node[internalInstanceKey] = hostInst;
22
}
23
21
-/**
22
- * Given a DOM node, return the closest ReactDOMComponent or
23
- * ReactDOMTextComponent instance ancestor.
24
- */
25
-export function getClosestInstanceFromNode(node) {
26
- let inst = node[internalInstanceKey];
27
- if (inst) {
28
- return inst;
29
- }
24
+export function markContainerAsRoot(hostRoot, node) {
25
+ node[internalContainerInstanceKey] = hostRoot;
26
+}
27
31
- do {
32
- node = node.parentNode;
33
- if (node) {
34
- inst = node[internalInstanceKey];
35
- } else {
36
- // Top of the tree. This node must not be part of a React tree (or is
37
- // unmounted, potentially).
38
- return null;
28
+// Given a DOM node, return the closest HostComponent or HostText fiber ancestor.
29
+// If the target node is part of a hydrated or not yet rendered subtree, then
30
+// this may also return a SuspenseComponent or HostRoot to indicate that.
31
+// Conceptually the HostRoot fiber is a child of the Container node. So if you
32
+// pass the Container node as the targetNode, you wiill not actually get the
33
+// HostRoot back. To get to the HostRoot, you need to pass a child of it.
34
+// The same thing applies to Suspense boundaries.
35
+export function getClosestInstanceFromNode(targetNode) {
36
+ let targetInst = targetNode[internalInstanceKey];
37
+ if (targetInst) {
38
+ // Don't return HostRoot or SuspenseComponent here.
39
+ return targetInst;
40
+ }
41
+ // If the direct event target isn't a React owned DOM node, we need to look
42
+ // to see if one of its parents is a React owned DOM node.
43
+ let parentNode = targetNode.parentNode;
44
+ while (parentNode) {
45
+ // We'll check if this is a container root that could include
46
+ // React nodes in the future. We need to check this first because
47
+ // if we're a child of a dehydrated container, we need to first
48
+ // find that inner container before moving on to finding the parent
49
+ // instance. Note that we don't check this field on the targetNode
50
+ // itself because the fibers are conceptually between the container
51
+ // node and the first child. It isn't surrounding the container node.
52
+ targetInst = parentNode[internalContainerInstanceKey];
53
+ if (targetInst) {
54
+ // If so, we return the HostRoot Fiber.
55
+ return targetInst;
56
}
40
- } while (!inst);
57
+ targetInst = parentNode[internalInstanceKey];
58
+ if (targetInst) {
59
+ // Since this wasn't the direct target of the event, we might have
60
+ // stepped past dehydrated DOM nodes to get here. However they could
61
+ // also have been non-React nodes. We need to answer which one.
62
42
- let tag = inst.tag;
43
- switch (tag) {
44
- case HostComponent:
45
- case HostText:
46
- // In Fiber, this will always be the deepest root.
47
- return inst;
63
+ // If we the instance doesn't have any children, then there can't be
64
+ // a nested suspense boundary within it. So we can use this as a fast
65
+ // bailout. Most of the time, when people add non-React children to
66
+ // the tree, it is using a ref to a child-less DOM node.
67
+ // We only need to check one of the fibers because if it has ever
68
+ // gone from having children to deleting them or vice versa it would
69
+ // have deleted the dehydrated boundary nested inside already.
70
+ if (targetInst.child !== null) {
71
+ // Next we need to figure out if the node that skipped past is
72
+ // nested within a dehydrated boundary and if so, which one.
73
+ let suspenseInstance = getParentSuspenseInstance(targetNode);
74
+ if (suspenseInstance !== null) {
75
+ // We found a suspense instance. That means that we haven't
76
+ // hydrated it yet. Even though we leave the comments in the
77
+ // DOM after hydrating, and there are boundaries in the DOM
78
+ // that could already be hydrated, we wouldn't have found them
79
+ // through this pass since if the target is hydrated it would
80
+ // have had an internalInstanceKey on it.
81
+ // Let's get the fiber associated with the SuspenseComponent
82
+ // as the deepest instance.
83
+ let targetSuspenseInst = suspenseInstance[internalInstanceKey];
84
+ if (targetSuspenseInst) {
85
+ return targetSuspenseInst;
86
+ }
87
+ // If we don't find a Fiber on the comment, it might be because
88
+ // we haven't gotten to hydrate it yet. That should mean that
89
+ // the parent component also hasn't hydrated yet but we can
90
+ // just return that since it will bail out on the isMounted
91
+ // check.
92
+ }
93
+ }
94
+ return targetInst;
95
+ }
96
+ targetNode = parentNode;
97
+ parentNode = targetNode.parentNode;
98
}
99
return null;
100
}