main
js 236 lines 6.62 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 {ReactNodeList, ReactFormState} from 'shared/ReactTypes';
11 import type {
12 FiberRoot,
13 SuspenseHydrationCallbacks,
14 TransitionTracingCallbacks,
15 } from './ReactInternalTypes';
16 import type {RootTag} from './ReactRootTags';
17 import type {Cache} from './ReactFiberCacheComponent';
18 import type {Container} from './ReactFiberConfig';
19
20 import {noTimeout} from './ReactFiberConfig';
21 import {createHostRootFiber} from './ReactFiber';
22 import {
23 NoLane,
24 NoLanes,
25 NoTimestamp,
26 TotalLanes,
27 createLaneMap,
28 } from './ReactFiberLane';
29 import {
30 enableSuspenseCallback,
31 enableProfilerCommitHooks,
32 enableProfilerTimer,
33 enableUpdaterTracking,
34 enableTransitionTracing,
35 disableLegacyMode,
36 enableViewTransition,
37 enableGestureTransition,
38 enableDefaultTransitionIndicator,
39 } from 'shared/ReactFeatureFlags';
40 import {initializeUpdateQueue} from './ReactFiberClassUpdateQueue';
41 import {LegacyRoot, ConcurrentRoot} from './ReactRootTags';
42 import {createCache, retainCache} from './ReactFiberCacheComponent';
43
44 export type RootState = {
45 element: any,
46 isDehydrated: boolean,
47 cache: Cache,
48 };
49
50 function FiberRootNode(
51 this: $FlowFixMe,
52 containerInfo: any,
53 // $FlowFixMe[missing-local-annot]
54 tag,
55 hydrate: any,
56 identifierPrefix: any,
57 onUncaughtError: any,
58 onCaughtError: any,
59 onRecoverableError: any,
60 onDefaultTransitionIndicator: any,
61 formState: ReactFormState<any, any> | null,
62 ) {
63 this.tag = disableLegacyMode ? ConcurrentRoot : tag;
64 this.containerInfo = containerInfo;
65 this.pendingChildren = null;
66 this.current = null;
67 this.pingCache = null;
68 this.timeoutHandle = noTimeout;
69 this.cancelPendingCommit = null;
70 this.context = null;
71 this.pendingContext = null;
72 this.next = null;
73 this.callbackNode = null;
74 this.callbackPriority = NoLane;
75 this.expirationTimes = createLaneMap(NoTimestamp);
76
77 this.pendingLanes = NoLanes;
78 this.suspendedLanes = NoLanes;
79 this.pingedLanes = NoLanes;
80 this.warmLanes = NoLanes;
81 this.expiredLanes = NoLanes;
82 if (enableDefaultTransitionIndicator) {
83 this.indicatorLanes = NoLanes;
84 }
85 this.errorRecoveryDisabledLanes = NoLanes;
86 this.shellSuspendCounter = 0;
87
88 this.entangledLanes = NoLanes;
89 this.entanglements = createLaneMap(NoLanes);
90
91 this.hiddenUpdates = createLaneMap(null);
92
93 this.identifierPrefix = identifierPrefix;
94 this.onUncaughtError = onUncaughtError;
95 this.onCaughtError = onCaughtError;
96 this.onRecoverableError = onRecoverableError;
97
98 if (enableDefaultTransitionIndicator) {
99 this.onDefaultTransitionIndicator = onDefaultTransitionIndicator;
100 this.pendingIndicator = null;
101 }
102
103 this.pooledCache = null;
104 this.pooledCacheLanes = NoLanes;
105
106 if (enableSuspenseCallback) {
107 this.hydrationCallbacks = null;
108 }
109
110 this.formState = formState;
111
112 if (enableViewTransition) {
113 this.transitionTypes = null;
114 }
115
116 if (enableGestureTransition) {
117 this.pendingGestures = null;
118 this.gestureClone = null;
119 }
120
121 this.incompleteTransitions = new Map();
122 if (enableTransitionTracing) {
123 this.transitionCallbacks = null;
124 this.transitionLanes = createLaneMap(null);
125 }
126
127 if (enableProfilerTimer && enableProfilerCommitHooks) {
128 this.effectDuration = -0;
129 this.passiveEffectDuration = -0;
130 }
131
132 if (enableUpdaterTracking) {
133 this.memoizedUpdaters = new Set();
134 const pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []);
135 for (let i = 0; i < TotalLanes; i++) {
136 pendingUpdatersLaneMap.push(new Set());
137 }
138 }
139
140 if (__DEV__) {
141 if (disableLegacyMode) {
142 // TODO: This varies by each renderer.
143 this._debugRootType = hydrate ? 'hydrateRoot()' : 'createRoot()';
144 } else {
145 switch (tag) {
146 case ConcurrentRoot:
147 this._debugRootType = hydrate ? 'hydrateRoot()' : 'createRoot()';
148 break;
149 case LegacyRoot:
150 this._debugRootType = hydrate ? 'hydrate()' : 'render()';
151 break;
152 }
153 }
154 }
155 }
156
157 export function createFiberRoot(
158 containerInfo: Container,
159 tag: RootTag,
160 hydrate: boolean,
161 initialChildren: ReactNodeList,
162 hydrationCallbacks: null | SuspenseHydrationCallbacks,
163 isStrictMode: boolean,
164 // TODO: We have several of these arguments that are conceptually part of the
165 // host config, but because they are passed in at runtime, we have to thread
166 // them through the root constructor. Perhaps we should put them all into a
167 // single type, like a DynamicHostConfig that is defined by the renderer.
168 identifierPrefix: string,
169 formState: ReactFormState<any, any> | null,
170 onUncaughtError: (
171 error: mixed,
172 errorInfo: {+componentStack?: ?string},
173 ) => void,
174 onCaughtError: (
175 error: mixed,
176 errorInfo: {
177 +componentStack?: ?string,
178 +errorBoundary?: ?component(...props: any),
179 },
180 ) => void,
181 onRecoverableError: (
182 error: mixed,
183 errorInfo: {+componentStack?: ?string},
184 ) => void,
185 onDefaultTransitionIndicator: () => void | (() => void),
186 transitionCallbacks: null | TransitionTracingCallbacks,
187 ): FiberRoot {
188 // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
189 const root: FiberRoot = new FiberRootNode(
190 containerInfo,
191 tag,
192 hydrate,
193 identifierPrefix,
194 onUncaughtError,
195 onCaughtError,
196 onRecoverableError,
197 onDefaultTransitionIndicator,
198 formState,
199 ) as any;
200 if (enableSuspenseCallback) {
201 root.hydrationCallbacks = hydrationCallbacks;
202 }
203
204 if (enableTransitionTracing) {
205 root.transitionCallbacks = transitionCallbacks;
206 }
207
208 // Cyclic construction. This cheats the type system right now because
209 // stateNode is any.
210 const uninitializedFiber = createHostRootFiber(tag, isStrictMode);
211 root.current = uninitializedFiber;
212 uninitializedFiber.stateNode = root;
213
214 const initialCache = createCache();
215 retainCache(initialCache);
216
217 // The pooledCache is a fresh cache instance that is used temporarily
218 // for newly mounted boundaries during a render. In general, the
219 // pooledCache is always cleared from the root at the end of a render:
220 // it is either released when render commits, or moved to an Offscreen
221 // component if rendering suspends. Because the lifetime of the pooled
222 // cache is distinct from the main memoizedState.cache, it must be
223 // retained separately.
224 root.pooledCache = initialCache;
225 retainCache(initialCache);
226 const initialState: RootState = {
227 element: initialChildren,
228 isDehydrated: hydrate,
229 cache: initialCache,
230 };
231 uninitializedFiber.memoizedState = initialState;
232
233 initializeUpdateQueue(uninitializedFiber);
234
235 return root;
236 }