| 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 { |
| 11 | RefObject, |
| 12 | ReactContext, |
| 13 | StartTransitionOptions, |
| 14 | Wakeable, |
| 15 | Usable, |
| 16 | ReactFormState, |
| 17 | Awaited, |
| 18 | ReactComponentInfo, |
| 19 | ReactDebugInfo, |
| 20 | ReactKey, |
| 21 | } from 'shared/ReactTypes'; |
| 22 | import type {TransitionTypes} from 'react/src/ReactTransitionType'; |
| 23 | import type {WorkTag} from './ReactWorkTags'; |
| 24 | import type {TypeOfMode} from './ReactTypeOfMode'; |
| 25 | import type {Flags} from './ReactFiberFlags'; |
| 26 | import type {Lane, Lanes, LaneMap} from './ReactFiberLane'; |
| 27 | import type {RootTag} from './ReactRootTags'; |
| 28 | import type { |
| 29 | Container, |
| 30 | Instance, |
| 31 | TimeoutHandle, |
| 32 | NoTimeout, |
| 33 | ActivityInstance, |
| 34 | SuspenseInstance, |
| 35 | TransitionStatus, |
| 36 | } from './ReactFiberConfig'; |
| 37 | import type {Cache} from './ReactFiberCacheComponent'; |
| 38 | import type {Transition} from 'react/src/ReactStartTransition'; |
| 39 | import type {TracingMarkerInstance} from './ReactFiberTracingMarkerComponent'; |
| 40 | import type {ConcurrentUpdate} from './ReactFiberConcurrentUpdates'; |
| 41 | import type {ComponentStackNode} from 'react-server/src/ReactFizzComponentStack'; |
| 42 | import type {ThenableState} from './ReactFiberThenable'; |
| 43 | import type {ScheduledGesture} from './ReactFiberGestureScheduler'; |
| 44 | |
| 45 | // Unwind Circular: moved from ReactFiberHooks.old |
| 46 | export type HookType = |
| 47 | | 'useState' |
| 48 | | 'useReducer' |
| 49 | | 'useContext' |
| 50 | | 'useRef' |
| 51 | | 'useEffect' |
| 52 | | 'useEffectEvent' |
| 53 | | 'useInsertionEffect' |
| 54 | | 'useLayoutEffect' |
| 55 | | 'useCallback' |
| 56 | | 'useMemo' |
| 57 | | 'useImperativeHandle' |
| 58 | | 'useDebugValue' |
| 59 | | 'useDeferredValue' |
| 60 | | 'useTransition' |
| 61 | | 'useSyncExternalStore' |
| 62 | | 'useId' |
| 63 | | 'useCacheRefresh' |
| 64 | | 'useOptimistic' |
| 65 | | 'useFormState' |
| 66 | | 'useActionState'; |
| 67 | |
| 68 | export type ContextDependency<T> = { |
| 69 | context: ReactContext<T>, |
| 70 | next: ContextDependency<mixed> | null, |
| 71 | memoizedValue: T, |
| 72 | ... |
| 73 | }; |
| 74 | |
| 75 | export type Dependencies = { |
| 76 | lanes: Lanes, |
| 77 | firstContext: ContextDependency<mixed> | null, |
| 78 | _debugThenableState?: null | ThenableState, // DEV-only |
| 79 | ... |
| 80 | }; |
| 81 | |
| 82 | export type MemoCache = { |
| 83 | data: Array<Array<any>>, |
| 84 | index: number, |
| 85 | }; |
| 86 | |
| 87 | // A Fiber is work on a Component that needs to be done or was done. There can |
| 88 | // be more than one per component. |
| 89 | export type Fiber = { |
| 90 | // These first fields are conceptually members of an Instance. This used to |
| 91 | // be split into a separate type and intersected with the other Fiber fields, |
| 92 | // but until Flow fixes its intersection bugs, we've merged them into a |
| 93 | // single type. |
| 94 | |
| 95 | // An Instance is shared between all versions of a component. We can easily |
| 96 | // break this out into a separate object to avoid copying so much to the |
| 97 | // alternate versions of the tree. We put this on a single object for now to |
| 98 | // minimize the number of objects created during the initial render. |
| 99 | |
| 100 | // Tag identifying the type of fiber. |
| 101 | tag: WorkTag, |
| 102 | |
| 103 | // Unique identifier of this child. |
| 104 | key: ReactKey, |
| 105 | |
| 106 | // The value of element.type which is used to preserve the identity during |
| 107 | // reconciliation of this child. |
| 108 | elementType: any, |
| 109 | |
| 110 | // The resolved function/class/ associated with this fiber. |
| 111 | type: any, |
| 112 | |
| 113 | // The local state associated with this fiber. |
| 114 | stateNode: any, |
| 115 | |
| 116 | // Conceptual aliases |
| 117 | // parent : Instance -> return The parent happens to be the same as the |
| 118 | // return fiber since we've merged the fiber and instance. |
| 119 | |
| 120 | // Remaining fields belong to Fiber |
| 121 | |
| 122 | // The Fiber to return to after finishing processing this one. |
| 123 | // This is effectively the parent, but there can be multiple parents (two) |
| 124 | // so this is only the parent of the thing we're currently processing. |
| 125 | // It is conceptually the same as the return address of a stack frame. |
| 126 | return: Fiber | null, |
| 127 | |
| 128 | // Singly Linked List Tree Structure. |
| 129 | child: Fiber | null, |
| 130 | sibling: Fiber | null, |
| 131 | index: number, |
| 132 | |
| 133 | // The ref last used to attach this node. |
| 134 | // I'll avoid adding an owner field for prod and model that as functions. |
| 135 | ref: |
| 136 | | null |
| 137 | | (((handle: mixed) => void) & {_stringRef: ?string, ...}) |
| 138 | | RefObject, |
| 139 | |
| 140 | refCleanup: null | (() => void), |
| 141 | |
| 142 | // Input is the data coming into process this fiber. Arguments. Props. |
| 143 | pendingProps: any, // This type will be more specific once we overload the tag. |
| 144 | memoizedProps: any, // The props used to create the output. |
| 145 | |
| 146 | // A queue of state updates and callbacks. |
| 147 | updateQueue: mixed, |
| 148 | |
| 149 | // The state used to create the output |
| 150 | memoizedState: any, |
| 151 | |
| 152 | // Dependencies (contexts, events) for this fiber, if it has any |
| 153 | dependencies: Dependencies | null, |
| 154 | |
| 155 | // Bitfield that describes properties about the fiber and its subtree. E.g. |
| 156 | // the ConcurrentMode flag indicates whether the subtree should be async-by- |
| 157 | // default. When a fiber is created, it inherits the mode of its |
| 158 | // parent. Additional flags can be set at creation time, but after that the |
| 159 | // value should remain unchanged throughout the fiber's lifetime, particularly |
| 160 | // before its child fibers are created. |
| 161 | mode: TypeOfMode, |
| 162 | |
| 163 | // Effect |
| 164 | flags: Flags, |
| 165 | subtreeFlags: Flags, |
| 166 | deletions: Array<Fiber> | null, |
| 167 | |
| 168 | lanes: Lanes, |
| 169 | childLanes: Lanes, |
| 170 | |
| 171 | // This is a pooled version of a Fiber. Every fiber that gets updated will |
| 172 | // eventually have a pair. There are cases when we can clean up pairs to save |
| 173 | // memory if we need to. |
| 174 | alternate: Fiber | null, |
| 175 | |
| 176 | // Time spent rendering this Fiber and its descendants for the current update. |
| 177 | // This tells us how well the tree makes use of sCU for memoization. |
| 178 | // It is reset to 0 each time we render and only updated when we don't bailout. |
| 179 | // This field is only set when the enableProfilerTimer flag is enabled. |
| 180 | actualDuration?: number, |
| 181 | |
| 182 | // If the Fiber is currently active in the "render" phase, |
| 183 | // This marks the time at which the work began. |
| 184 | // This field is only set when the enableProfilerTimer flag is enabled. |
| 185 | actualStartTime?: number, |
| 186 | |
| 187 | // Duration of the most recent render time for this Fiber. |
| 188 | // This value is not updated when we bailout for memoization purposes. |
| 189 | // This field is only set when the enableProfilerTimer flag is enabled. |
| 190 | selfBaseDuration?: number, |
| 191 | |
| 192 | // Sum of base times for all descendants of this Fiber. |
| 193 | // This value bubbles up during the "complete" phase. |
| 194 | // This field is only set when the enableProfilerTimer flag is enabled. |
| 195 | treeBaseDuration?: number, |
| 196 | |
| 197 | // Conceptual aliases |
| 198 | // workInProgress : Fiber -> alternate The alternate used for reuse happens |
| 199 | // to be the same as work in progress. |
| 200 | // __DEV__ only |
| 201 | |
| 202 | _debugInfo?: ReactDebugInfo | null, |
| 203 | _debugOwner?: ReactComponentInfo | Fiber | null, |
| 204 | _debugStack?: Error | null, |
| 205 | _debugTask?: ConsoleTask | null, |
| 206 | _debugNeedsRemount?: boolean, |
| 207 | |
| 208 | // Used to verify that the order of hooks does not change between renders. |
| 209 | _debugHookTypes?: Array<HookType> | null, |
| 210 | }; |
| 211 | |
| 212 | type BaseFiberRootProperties = { |
| 213 | // The type of root (legacy, batched, concurrent, etc.) |
| 214 | tag: RootTag, |
| 215 | |
| 216 | // Any additional information from the host associated with this root. |
| 217 | containerInfo: Container, |
| 218 | // Used only by persistent updates. |
| 219 | pendingChildren: any, |
| 220 | // The currently active root fiber. This is the mutable root of the tree. |
| 221 | current: Fiber, |
| 222 | |
| 223 | pingCache: WeakMap<Wakeable, Set<mixed>> | Map<Wakeable, Set<mixed>> | null, |
| 224 | |
| 225 | // Timeout handle returned by setTimeout. Used to cancel a pending timeout, if |
| 226 | // it's superseded by a new one. |
| 227 | timeoutHandle: TimeoutHandle | NoTimeout, |
| 228 | // When a root has a pending commit scheduled, calling this function will |
| 229 | // cancel it. |
| 230 | // TODO: Can this be consolidated with timeoutHandle? |
| 231 | cancelPendingCommit: null | (() => void), |
| 232 | // Top context object, used by renderSubtreeIntoContainer |
| 233 | context: Object | null, |
| 234 | pendingContext: Object | null, |
| 235 | |
| 236 | // Used to create a linked list that represent all the roots that have |
| 237 | // pending work scheduled on them. |
| 238 | next: FiberRoot | null, |
| 239 | |
| 240 | // Node returned by Scheduler.scheduleCallback. Represents the next rendering |
| 241 | // task that the root will work on. |
| 242 | callbackNode: any, |
| 243 | callbackPriority: Lane, |
| 244 | expirationTimes: LaneMap<number>, |
| 245 | hiddenUpdates: LaneMap<Array<ConcurrentUpdate> | null>, |
| 246 | |
| 247 | pendingLanes: Lanes, |
| 248 | suspendedLanes: Lanes, |
| 249 | pingedLanes: Lanes, |
| 250 | warmLanes: Lanes, |
| 251 | expiredLanes: Lanes, |
| 252 | indicatorLanes: Lanes, // enableDefaultTransitionIndicator only |
| 253 | errorRecoveryDisabledLanes: Lanes, |
| 254 | shellSuspendCounter: number, |
| 255 | |
| 256 | entangledLanes: Lanes, |
| 257 | entanglements: LaneMap<Lanes>, |
| 258 | |
| 259 | pooledCache: Cache | null, |
| 260 | pooledCacheLanes: Lanes, |
| 261 | |
| 262 | // TODO: In Fizz, id generation is specific to each server config. Maybe we |
| 263 | // should do this in Fiber, too? Deferring this decision for now because |
| 264 | // there's no other place to store the prefix except for an internal field on |
| 265 | // the public createRoot object, which the fiber tree does not currently have |
| 266 | // a reference to. |
| 267 | identifierPrefix: string, |
| 268 | |
| 269 | onUncaughtError: ( |
| 270 | error: mixed, |
| 271 | errorInfo: {+componentStack?: ?string}, |
| 272 | ) => void, |
| 273 | onCaughtError: ( |
| 274 | error: mixed, |
| 275 | errorInfo: { |
| 276 | +componentStack?: ?string, |
| 277 | +errorBoundary?: ?component(...props: any), |
| 278 | }, |
| 279 | ) => void, |
| 280 | onRecoverableError: ( |
| 281 | error: mixed, |
| 282 | errorInfo: {+componentStack?: ?string}, |
| 283 | ) => void, |
| 284 | |
| 285 | // enableDefaultTransitionIndicator only |
| 286 | onDefaultTransitionIndicator: () => void | (() => void), |
| 287 | pendingIndicator: null | (() => void), |
| 288 | |
| 289 | formState: ReactFormState<any, any> | null, |
| 290 | |
| 291 | // enableViewTransition only |
| 292 | transitionTypes: null | TransitionTypes, // TODO: Make this a LaneMap. |
| 293 | // enableGestureTransition only |
| 294 | pendingGestures: null | ScheduledGesture, |
| 295 | gestureClone: null | Instance, |
| 296 | }; |
| 297 | |
| 298 | // The following attributes are only used by DevTools and are only present in DEV builds. |
| 299 | // They enable DevTools Profiler UI to show which Fiber(s) scheduled a given commit. |
| 300 | type UpdaterTrackingOnlyFiberRootProperties = { |
| 301 | memoizedUpdaters: Set<Fiber>, |
| 302 | pendingUpdatersLaneMap: LaneMap<Set<Fiber>>, |
| 303 | }; |
| 304 | |
| 305 | export type SuspenseHydrationCallbacks = { |
| 306 | +onHydrated?: ( |
| 307 | hydrationBoundary: SuspenseInstance | ActivityInstance, |
| 308 | ) => void, |
| 309 | +onDeleted?: (hydrationBoundary: SuspenseInstance | ActivityInstance) => void, |
| 310 | ... |
| 311 | }; |
| 312 | |
| 313 | // The follow fields are only used by enableSuspenseCallback for hydration. |
| 314 | type SuspenseCallbackOnlyFiberRootProperties = { |
| 315 | hydrationCallbacks: null | SuspenseHydrationCallbacks, |
| 316 | }; |
| 317 | |
| 318 | export type TransitionTracingCallbacks = { |
| 319 | onTransitionStart?: (transitionName: string, startTime: number) => void, |
| 320 | onTransitionProgress?: ( |
| 321 | transitionName: string, |
| 322 | startTime: number, |
| 323 | currentTime: number, |
| 324 | pending: Array<{name: null | string}>, |
| 325 | ) => void, |
| 326 | onTransitionIncomplete?: ( |
| 327 | transitionName: string, |
| 328 | startTime: number, |
| 329 | deletions: Array<{ |
| 330 | type: string, |
| 331 | name?: string | null, |
| 332 | endTime: number, |
| 333 | }>, |
| 334 | ) => void, |
| 335 | onTransitionComplete?: ( |
| 336 | transitionName: string, |
| 337 | startTime: number, |
| 338 | endTime: number, |
| 339 | ) => void, |
| 340 | onMarkerProgress?: ( |
| 341 | transitionName: string, |
| 342 | marker: string, |
| 343 | startTime: number, |
| 344 | currentTime: number, |
| 345 | pending: Array<{name: null | string}>, |
| 346 | ) => void, |
| 347 | onMarkerIncomplete?: ( |
| 348 | transitionName: string, |
| 349 | marker: string, |
| 350 | startTime: number, |
| 351 | deletions: Array<{ |
| 352 | type: string, |
| 353 | name?: string | null, |
| 354 | endTime: number, |
| 355 | }>, |
| 356 | ) => void, |
| 357 | onMarkerComplete?: ( |
| 358 | transitionName: string, |
| 359 | marker: string, |
| 360 | startTime: number, |
| 361 | endTime: number, |
| 362 | ) => void, |
| 363 | }; |
| 364 | |
| 365 | // The following fields are only used in transition tracing in Profile builds |
| 366 | type TransitionTracingOnlyFiberRootProperties = { |
| 367 | transitionCallbacks: null | TransitionTracingCallbacks, |
| 368 | transitionLanes: LaneMap<Set<Transition> | null>, |
| 369 | // Transitions on the root can be represented as a bunch of tracing markers. |
| 370 | // Each entangled group of transitions can be treated as a tracing marker. |
| 371 | // It will have a set of pending suspense boundaries. These transitions |
| 372 | // are considered complete when the pending suspense boundaries set is |
| 373 | // empty. We can represent this as a Map of transitions to suspense |
| 374 | // boundary sets |
| 375 | incompleteTransitions: Map<Transition, TracingMarkerInstance>, |
| 376 | }; |
| 377 | |
| 378 | type ProfilerCommitHooksOnlyFiberRootProperties = { |
| 379 | effectDuration: number, |
| 380 | passiveEffectDuration: number, |
| 381 | }; |
| 382 | |
| 383 | // Exported FiberRoot type includes all properties, |
| 384 | // To avoid requiring potentially error-prone :any casts throughout the project. |
| 385 | // The types are defined separately within this file to ensure they stay in sync. |
| 386 | export type FiberRoot = { |
| 387 | ...BaseFiberRootProperties, |
| 388 | ...SuspenseCallbackOnlyFiberRootProperties, |
| 389 | ...UpdaterTrackingOnlyFiberRootProperties, |
| 390 | ...TransitionTracingOnlyFiberRootProperties, |
| 391 | ...ProfilerCommitHooksOnlyFiberRootProperties, |
| 392 | }; |
| 393 | |
| 394 | type BasicStateAction<S> = (S => S) | S; |
| 395 | type Dispatch<A> = A => void; |
| 396 | |
| 397 | export type Dispatcher = { |
| 398 | use: <T>(Usable<T>) => T, |
| 399 | readContext<T>(context: ReactContext<T>): T, |
| 400 | useState<S>(initialState: (() => S) | S): [S, Dispatch<BasicStateAction<S>>], |
| 401 | useReducer<S, I, A>( |
| 402 | reducer: (S, A) => S, |
| 403 | initialArg: I, |
| 404 | init?: (I) => S, |
| 405 | ): [S, Dispatch<A>], |
| 406 | useContext<T>(context: ReactContext<T>): T, |
| 407 | useRef<T>(initialValue: T): {current: T}, |
| 408 | useEffect( |
| 409 | create: () => (() => void) | void, |
| 410 | deps: Array<mixed> | void | null, |
| 411 | ): void, |
| 412 | useEffectEvent: <Args, F: (...Array<Args>) => mixed>(callback: F) => F, |
| 413 | useInsertionEffect( |
| 414 | create: () => (() => void) | void, |
| 415 | deps: Array<mixed> | void | null, |
| 416 | ): void, |
| 417 | useLayoutEffect( |
| 418 | create: () => (() => void) | void, |
| 419 | deps: Array<mixed> | void | null, |
| 420 | ): void, |
| 421 | useCallback<T>(callback: T, deps: Array<mixed> | void | null): T, |
| 422 | useMemo<T>(nextCreate: () => T, deps: Array<mixed> | void | null): T, |
| 423 | useImperativeHandle<T>( |
| 424 | ref: {current: T | null} | ((inst: T | null) => mixed) | null | void, |
| 425 | create: () => T, |
| 426 | deps: Array<mixed> | void | null, |
| 427 | ): void, |
| 428 | useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void, |
| 429 | useDeferredValue<T>(value: T, initialValue?: T): T, |
| 430 | useTransition(): [ |
| 431 | boolean, |
| 432 | (callback: () => void, options?: StartTransitionOptions) => void, |
| 433 | ], |
| 434 | useSyncExternalStore<T>( |
| 435 | subscribe: (() => void) => () => void, |
| 436 | getSnapshot: () => T, |
| 437 | getServerSnapshot?: () => T, |
| 438 | ): T, |
| 439 | useId(): string, |
| 440 | useCacheRefresh: () => <T>(?() => T, ?T) => void, |
| 441 | useMemoCache: (size: number) => Array<any>, |
| 442 | useHostTransitionStatus: () => TransitionStatus, |
| 443 | useOptimistic: <S, A>( |
| 444 | passthrough: S, |
| 445 | reducer: ?(S, A) => S, |
| 446 | ) => [S, (A) => void], |
| 447 | useFormState: <S, P>( |
| 448 | action: (Awaited<S>, P) => S, |
| 449 | initialState: Awaited<S>, |
| 450 | permalink?: string, |
| 451 | ) => [Awaited<S>, (P) => void, boolean], |
| 452 | useActionState: <S, P>( |
| 453 | action: (Awaited<S>, P) => S, |
| 454 | initialState: Awaited<S>, |
| 455 | permalink?: string, |
| 456 | ) => [Awaited<S>, (P) => void, boolean], |
| 457 | }; |
| 458 | |
| 459 | export type AsyncDispatcher = { |
| 460 | getCacheForType: <T>(resourceType: () => T) => T, |
| 461 | cacheSignal: () => null | AbortSignal, |
| 462 | // DEV-only |
| 463 | getOwner: () => null | Fiber | ReactComponentInfo | ComponentStackNode, |
| 464 | }; |