main
js 216 lines 5.36 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 import type {Fiber} from './ReactInternalTypes';
11 import type {
12 ReactScopeInstance,
13 ReactContext,
14 ReactScopeQuery,
15 } from 'shared/ReactTypes';
16
17 import {
18 getPublicInstance,
19 getInstanceFromNode,
20 getInstanceFromScope,
21 } from './ReactFiberConfig';
22 import {isFiberSuspenseAndTimedOut} from './ReactFiberTreeReflection';
23
24 import {HostComponent, ScopeComponent, ContextProvider} from './ReactWorkTags';
25 import {enableScopeAPI} from 'shared/ReactFeatureFlags';
26
27 function getSuspenseFallbackChild(fiber: Fiber): Fiber | null {
28 return ((fiber.child as any as Fiber).sibling as any as Fiber).child;
29 }
30
31 const emptyObject = {};
32
33 function collectScopedNodes(
34 node: Fiber,
35 fn: ReactScopeQuery,
36 scopedNodes: Array<any>,
37 ): void {
38 if (enableScopeAPI) {
39 if (node.tag === HostComponent) {
40 const {type, memoizedProps, stateNode} = node;
41 const instance = getPublicInstance(stateNode);
42 if (
43 // $FlowFixMe[invalid-compare]
44 instance !== null &&
45 fn(type, memoizedProps || emptyObject, instance) === true
46 ) {
47 scopedNodes.push(instance);
48 }
49 }
50 let child = node.child;
51
52 if (isFiberSuspenseAndTimedOut(node)) {
53 child = getSuspenseFallbackChild(node);
54 }
55 if (child !== null) {
56 collectScopedNodesFromChildren(child, fn, scopedNodes);
57 }
58 }
59 }
60
61 function collectFirstScopedNode(
62 node: Fiber,
63 fn: ReactScopeQuery,
64 ): null | Object {
65 if (enableScopeAPI) {
66 if (node.tag === HostComponent) {
67 const {type, memoizedProps, stateNode} = node;
68 const instance = getPublicInstance(stateNode);
69 // $FlowFixMe[invalid-compare]
70 if (instance !== null && fn(type, memoizedProps, instance) === true) {
71 return instance;
72 }
73 }
74 let child = node.child;
75
76 if (isFiberSuspenseAndTimedOut(node)) {
77 child = getSuspenseFallbackChild(node);
78 }
79 if (child !== null) {
80 return collectFirstScopedNodeFromChildren(child, fn);
81 }
82 }
83 return null;
84 }
85
86 function collectScopedNodesFromChildren(
87 startingChild: Fiber,
88 fn: ReactScopeQuery,
89 scopedNodes: Array<any>,
90 ): void {
91 let child: null | Fiber = startingChild;
92 while (child !== null) {
93 collectScopedNodes(child, fn, scopedNodes);
94 child = child.sibling;
95 }
96 }
97
98 function collectFirstScopedNodeFromChildren(
99 startingChild: Fiber,
100 fn: ReactScopeQuery,
101 ): Object | null {
102 let child: null | Fiber = startingChild;
103 while (child !== null) {
104 const scopedNode = collectFirstScopedNode(child, fn);
105 if (scopedNode !== null) {
106 return scopedNode;
107 }
108 child = child.sibling;
109 }
110 return null;
111 }
112
113 function collectNearestContextValues<T>(
114 node: Fiber,
115 context: ReactContext<T>,
116 childContextValues: Array<T>,
117 ): void {
118 if (node.tag === ContextProvider && node.type === context) {
119 const contextValue = node.memoizedProps.value;
120 childContextValues.push(contextValue);
121 } else {
122 let child = node.child;
123
124 if (isFiberSuspenseAndTimedOut(node)) {
125 child = getSuspenseFallbackChild(node);
126 }
127 if (child !== null) {
128 collectNearestChildContextValues(child, context, childContextValues);
129 }
130 }
131 }
132
133 function collectNearestChildContextValues<T>(
134 startingChild: Fiber | null,
135 context: ReactContext<T>,
136 childContextValues: Array<T>,
137 ): void {
138 let child = startingChild;
139 while (child !== null) {
140 collectNearestContextValues(child, context, childContextValues);
141 child = child.sibling;
142 }
143 }
144
145 function DO_NOT_USE_queryAllNodes(
146 this: $FlowFixMe,
147 fn: ReactScopeQuery,
148 ): null | Array<Object> {
149 const currentFiber = getInstanceFromScope(this);
150 // $FlowFixMe[invalid-compare]
151 if (currentFiber === null) {
152 return null;
153 }
154 const child = currentFiber.child;
155 const scopedNodes: Array<any> = [];
156 // $FlowFixMe[invalid-compare]
157 if (child !== null) {
158 collectScopedNodesFromChildren(child, fn, scopedNodes);
159 }
160 return scopedNodes.length === 0 ? null : scopedNodes;
161 }
162
163 function DO_NOT_USE_queryFirstNode(
164 this: $FlowFixMe,
165 fn: ReactScopeQuery,
166 ): null | Object {
167 const currentFiber = getInstanceFromScope(this);
168 // $FlowFixMe[invalid-compare]
169 if (currentFiber === null) {
170 return null;
171 }
172 const child = currentFiber.child;
173 // $FlowFixMe[invalid-compare]
174 if (child !== null) {
175 return collectFirstScopedNodeFromChildren(child, fn);
176 }
177 return null;
178 }
179
180 function containsNode(this: $FlowFixMe, node: Object): boolean {
181 let fiber = getInstanceFromNode(node);
182 while (fiber !== null) {
183 if (fiber.tag === ScopeComponent && fiber.stateNode === this) {
184 return true;
185 }
186 fiber = fiber.return;
187 }
188 return false;
189 }
190
191 function getChildContextValues<T>(
192 this: $FlowFixMe,
193 context: ReactContext<T>,
194 ): Array<T> {
195 const currentFiber = getInstanceFromScope(this);
196 // $FlowFixMe[invalid-compare]
197 if (currentFiber === null) {
198 return [];
199 }
200 const child = currentFiber.child;
201 const childContextValues: Array<T> = [];
202 // $FlowFixMe[invalid-compare]
203 if (child !== null) {
204 collectNearestChildContextValues(child, context, childContextValues);
205 }
206 return childContextValues;
207 }
208
209 export function createScopeInstance(): ReactScopeInstance {
210 return {
211 DO_NOT_USE_queryAllNodes,
212 DO_NOT_USE_queryFirstNode,
213 containsNode,
214 getChildContextValues,
215 };
216 }