mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
* add transition name to startTransition
Add a transitionName to start transition, store the transition start time and name in the batch config, and pass it to the root on render
* Transition Tracing Types and Consts
* Root begin work
The root operates as a tracing marker that has all transitions on it. This PR only tested the root with one transition so far
- Store transitions in memoizedState. Do this in updateHostRoot AND attemptEarlyBailoutIfNoScheduledUpdate. We need to do this in the latter part because even if the root itself doesn't have an update, it could still have new transitions in its transitionLanes map that we need to process.
* Transition Tracing commit phase
- adds a module scoped pending transition callbacks object that contains all transition callbacks that have not yet been processed. This contains all callbacks before the next paint occurs.
- Add code in the mutation phase to:
* For the root, if there are transitions that were initialized during this commit in the root transition lanes map, add a transition start call to the pending transition callbacks object. Then, remove the transitions from the root transition lanes map.
* For roots, in the commit phase, add a transition complete call
We add this code in the mutation phase because we can't add it to the passive phase because then the paint might have occurred before we even know which callbacks to call
* Process Callbacks after paint
At the end of the commit phase, call scheduleTransitionCallbacks to schedule all pending transition callbacks to be called after paint. Then clear the callbacks
199 lines
5.5 KiB
JavaScript
199 lines
5.5 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {
|
|
FiberRoot,
|
|
SuspenseHydrationCallbacks,
|
|
TransitionTracingCallbacks,
|
|
} from './ReactInternalTypes';
|
|
import type {RootTag} from './ReactRootTags';
|
|
import type {Cache} from './ReactFiberCacheComponent.old';
|
|
import type {Transitions} from './ReactFiberTracingMarkerComponent.old';
|
|
|
|
import {noTimeout, supportsHydration} from './ReactFiberHostConfig';
|
|
import {createHostRootFiber} from './ReactFiber.old';
|
|
import {
|
|
NoLane,
|
|
NoLanes,
|
|
NoTimestamp,
|
|
TotalLanes,
|
|
createLaneMap,
|
|
} from './ReactFiberLane.old';
|
|
import {
|
|
enableSuspenseCallback,
|
|
enableCache,
|
|
enableProfilerCommitHooks,
|
|
enableProfilerTimer,
|
|
enableUpdaterTracking,
|
|
enableTransitionTracing,
|
|
} from 'shared/ReactFeatureFlags';
|
|
import {initializeUpdateQueue} from './ReactUpdateQueue.old';
|
|
import {LegacyRoot, ConcurrentRoot} from './ReactRootTags';
|
|
import {createCache, retainCache} from './ReactFiberCacheComponent.old';
|
|
|
|
export type RootState = {
|
|
element: any,
|
|
cache: Cache | null,
|
|
transitions: Transitions | null,
|
|
};
|
|
|
|
function FiberRootNode(
|
|
containerInfo,
|
|
tag,
|
|
hydrate,
|
|
identifierPrefix,
|
|
onRecoverableError,
|
|
) {
|
|
this.tag = tag;
|
|
this.containerInfo = containerInfo;
|
|
this.pendingChildren = null;
|
|
this.current = null;
|
|
this.pingCache = null;
|
|
this.finishedWork = null;
|
|
this.timeoutHandle = noTimeout;
|
|
this.context = null;
|
|
this.pendingContext = null;
|
|
this.isDehydrated = hydrate;
|
|
this.callbackNode = null;
|
|
this.callbackPriority = NoLane;
|
|
this.eventTimes = createLaneMap(NoLanes);
|
|
this.expirationTimes = createLaneMap(NoTimestamp);
|
|
|
|
this.pendingLanes = NoLanes;
|
|
this.suspendedLanes = NoLanes;
|
|
this.pingedLanes = NoLanes;
|
|
this.expiredLanes = NoLanes;
|
|
this.mutableReadLanes = NoLanes;
|
|
this.finishedLanes = NoLanes;
|
|
|
|
this.entangledLanes = NoLanes;
|
|
this.entanglements = createLaneMap(NoLanes);
|
|
|
|
this.identifierPrefix = identifierPrefix;
|
|
this.onRecoverableError = onRecoverableError;
|
|
|
|
if (enableCache) {
|
|
this.pooledCache = null;
|
|
this.pooledCacheLanes = NoLanes;
|
|
}
|
|
|
|
if (supportsHydration) {
|
|
this.mutableSourceEagerHydrationData = null;
|
|
}
|
|
|
|
if (enableSuspenseCallback) {
|
|
this.hydrationCallbacks = null;
|
|
}
|
|
|
|
if (enableTransitionTracing) {
|
|
this.transitionCallbacks = null;
|
|
const transitionLanesMap = (this.transitionLanes = []);
|
|
for (let i = 0; i < TotalLanes; i++) {
|
|
transitionLanesMap.push(null);
|
|
}
|
|
}
|
|
|
|
if (enableProfilerTimer && enableProfilerCommitHooks) {
|
|
this.effectDuration = 0;
|
|
this.passiveEffectDuration = 0;
|
|
}
|
|
|
|
if (enableUpdaterTracking) {
|
|
this.memoizedUpdaters = new Set();
|
|
const pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []);
|
|
for (let i = 0; i < TotalLanes; i++) {
|
|
pendingUpdatersLaneMap.push(new Set());
|
|
}
|
|
}
|
|
|
|
if (__DEV__) {
|
|
switch (tag) {
|
|
case ConcurrentRoot:
|
|
this._debugRootType = hydrate ? 'hydrateRoot()' : 'createRoot()';
|
|
break;
|
|
case LegacyRoot:
|
|
this._debugRootType = hydrate ? 'hydrate()' : 'render()';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createFiberRoot(
|
|
containerInfo: any,
|
|
tag: RootTag,
|
|
hydrate: boolean,
|
|
hydrationCallbacks: null | SuspenseHydrationCallbacks,
|
|
isStrictMode: boolean,
|
|
concurrentUpdatesByDefaultOverride: null | boolean,
|
|
// TODO: We have several of these arguments that are conceptually part of the
|
|
// host config, but because they are passed in at runtime, we have to thread
|
|
// them through the root constructor. Perhaps we should put them all into a
|
|
// single type, like a DynamicHostConfig that is defined by the renderer.
|
|
identifierPrefix: string,
|
|
onRecoverableError: null | ((error: mixed) => void),
|
|
transitionCallbacks: null | TransitionTracingCallbacks,
|
|
): FiberRoot {
|
|
const root: FiberRoot = (new FiberRootNode(
|
|
containerInfo,
|
|
tag,
|
|
hydrate,
|
|
identifierPrefix,
|
|
onRecoverableError,
|
|
): any);
|
|
if (enableSuspenseCallback) {
|
|
root.hydrationCallbacks = hydrationCallbacks;
|
|
}
|
|
|
|
if (enableTransitionTracing) {
|
|
root.transitionCallbacks = transitionCallbacks;
|
|
}
|
|
|
|
// Cyclic construction. This cheats the type system right now because
|
|
// stateNode is any.
|
|
const uninitializedFiber = createHostRootFiber(
|
|
tag,
|
|
isStrictMode,
|
|
concurrentUpdatesByDefaultOverride,
|
|
);
|
|
root.current = uninitializedFiber;
|
|
uninitializedFiber.stateNode = root;
|
|
|
|
if (enableCache) {
|
|
const initialCache = createCache();
|
|
retainCache(initialCache);
|
|
|
|
// The pooledCache is a fresh cache instance that is used temporarily
|
|
// for newly mounted boundaries during a render. In general, the
|
|
// pooledCache is always cleared from the root at the end of a render:
|
|
// it is either released when render commits, or moved to an Offscreen
|
|
// component if rendering suspends. Because the lifetime of the pooled
|
|
// cache is distinct from the main memoizedState.cache, it must be
|
|
// retained separately.
|
|
root.pooledCache = initialCache;
|
|
retainCache(initialCache);
|
|
const initialState: RootState = {
|
|
element: null,
|
|
cache: initialCache,
|
|
transitions: null,
|
|
};
|
|
uninitializedFiber.memoizedState = initialState;
|
|
} else {
|
|
const initialState: RootState = {
|
|
element: null,
|
|
cache: null,
|
|
transitions: null,
|
|
};
|
|
uninitializedFiber.memoizedState = initialState;
|
|
}
|
|
|
|
initializeUpdateQueue(uninitializedFiber);
|
|
|
|
return root;
|
|
}
|