Add Flow types for Fiber

Fixes https://github.com/bvaughn/react-devtools-experimental/issues/165
This commit is contained in:
Ivan Babak
2019-04-28 04:18:27 -07:00
parent 2118b1fe1b
commit 9f06bc345f
3 changed files with 160 additions and 35 deletions
+1
View File
@@ -26,6 +26,7 @@ shells/dev/build/*
[options]
esproposal.class_instance_fields=enable
esproposal.optional_chaining=enable
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue
suppress_comment=\\(.\\|\n\\)*\\$FlowIgnore
+77 -33
View File
@@ -573,7 +573,6 @@ export function attach(
}
}
// eslint-disable-next-line no-unused-vars
function haveProfilerTimesChanged(
prevFiber: Fiber,
nextFiber: Fiber
@@ -715,7 +714,7 @@ export function attach(
const isProfilingSupported = fiber.hasOwnProperty('treeBaseDuration');
if (isProfilingSupported) {
idToRootMap.set(id, currentRootID);
idToTreeBaseDurationMap.set(id, fiber.treeBaseDuration);
idToTreeBaseDurationMap.set(id, fiber.treeBaseDuration || 0);
}
const hasOwnerMetadata = fiber.hasOwnProperty('_debugOwner');
@@ -732,7 +731,9 @@ export function attach(
const ownerID =
_debugOwner != null ? getFiberID(getPrimaryFiber(_debugOwner)) : 0;
const parentID = getFiberID(getPrimaryFiber(parentFiber));
const parentID = parentFiber
? getFiberID(getPrimaryFiber(parentFiber))
: 0;
let displayNameStringID = getStringID(displayName);
let keyStringID = getStringID(key);
@@ -748,14 +749,14 @@ export function attach(
if (isProfiling) {
// Tree base duration updates are included in the operations typed array.
// So we have to convert them from milliseconds to microseconds so we can send them as ints.
const treeBaseDuration = Math.floor(fiber.treeBaseDuration * 1000);
const treeBaseDuration = Math.floor((fiber.treeBaseDuration || 0) * 1000);
pushOperation(TREE_OPERATION_UPDATE_TREE_BASE_DURATION);
pushOperation(id);
pushOperation(treeBaseDuration);
const { actualDuration } = fiber;
if (actualDuration > 0) {
if (actualDuration != null && actualDuration > 0) {
// If profiling is active, store durations for elements that were rendered during the commit.
const metadata = ((currentCommitProfilingMetadata: any): CommitProfilingData);
metadata.actualDurations.push(id, actualDuration);
@@ -849,8 +850,12 @@ export function attach(
// get the fallback child from the inner fragment and mount
// it as if it was our own child. Updates handle this too.
const primaryChildFragment = fiber.child;
const fallbackChildFragment = primaryChildFragment.sibling;
const fallbackChild = fallbackChildFragment.child;
const fallbackChildFragment = primaryChildFragment
? primaryChildFragment.sibling
: null;
const fallbackChild = fallbackChildFragment
? fallbackChildFragment.child
: null;
if (fallbackChild !== null) {
mountFiberRecursively(
fallbackChild,
@@ -893,9 +898,11 @@ export function attach(
if (isTimedOutSuspense) {
// If it's showing fallback tree, let's traverse it instead.
const primaryChildFragment = fiber.child;
const fallbackChildFragment = primaryChildFragment.sibling;
const fallbackChildFragment = primaryChildFragment
? primaryChildFragment.sibling
: null;
// Skip over to the real Fiber child.
child = fallbackChildFragment.child;
child = fallbackChildFragment ? fallbackChildFragment.child : null;
}
while (child !== null) {
@@ -913,20 +920,27 @@ export function attach(
const id = getFiberID(getPrimaryFiber(fiber));
const { actualDuration, treeBaseDuration } = fiber;
idToTreeBaseDurationMap.set(id, fiber.treeBaseDuration);
idToTreeBaseDurationMap.set(id, fiber.treeBaseDuration || 0);
if (isProfiling) {
if (treeBaseDuration !== fiber.alternate.treeBaseDuration) {
const { alternate } = fiber;
if (
treeBaseDuration !==
(alternate ? alternate.treeBaseDuration : undefined)
) {
// Tree base duration updates are included in the operations typed array.
// So we have to convert them from milliseconds to microseconds so we can send them as ints.
const treeBaseDuration = Math.floor(fiber.treeBaseDuration * 1000);
const treeBaseDuration = Math.floor(
(fiber.treeBaseDuration || 0) * 1000
);
pushOperation(TREE_OPERATION_UPDATE_TREE_BASE_DURATION);
pushOperation(getFiberID(getPrimaryFiber(fiber)));
pushOperation(treeBaseDuration);
}
if (haveProfilerTimesChanged(fiber.alternate, fiber)) {
if (actualDuration > 0) {
if (alternate ? haveProfilerTimesChanged(alternate, fiber) : true) {
if (actualDuration != null && actualDuration > 0) {
// If profiling is active, store durations for elements that were rendered during the commit.
const metadata = ((currentCommitProfilingMetadata: any): CommitProfilingData);
metadata.actualDurations.push(id, actualDuration);
@@ -1008,11 +1022,19 @@ export function attach(
if (prevDidTimeout && nextDidTimeOut) {
// Fallback -> Fallback:
// 1. Reconcile fallback set.
const nextFallbackChildSet = nextFiber.child.sibling;
const nextFiberChild = nextFiber.child;
const nextFallbackChildSet = nextFiberChild
? nextFiberChild.sibling
: null;
// Note: We can't use nextFiber.child.sibling.alternate
// because the set is special and alternate may not exist.
const prevFallbackChildSet = prevFiber.child.sibling;
const prevFiberChild = prevFiber.child;
const prevFallbackChildSet = prevFiberChild
? prevFiberChild.sibling
: null;
if (
nextFallbackChildSet != null &&
prevFallbackChildSet != null &&
updateFiberRecursively(
nextFallbackChildSet,
prevFallbackChildSet,
@@ -1038,9 +1060,14 @@ export function attach(
// We need to manually walk the previous tree and record unmounts.
unmountFiberChildrenRecursively(prevFiber);
// 2. Mount fallback set
const nextFallbackChildSet = nextFiber.child.sibling;
mountFiberRecursively(nextFallbackChildSet, nextFiber, true);
shouldResetChildren = true;
const nextFiberChild = nextFiber.child;
const nextFallbackChildSet = nextFiberChild
? nextFiberChild.sibling
: null;
if (nextFallbackChildSet != null) {
mountFiberRecursively(nextFallbackChildSet, nextFiber, true);
shouldResetChildren = true;
}
} else {
// Common case: Primary -> Primary.
// This is the same codepath as for non-Suspense fibers.
@@ -1109,9 +1136,12 @@ export function attach(
let nextChildSet = nextFiber.child;
if (nextDidTimeOut) {
// Special case: timed-out Suspense renders the fallback set.
nextChildSet = nextFiber.child.sibling;
const nextFiberChild = nextFiber.child;
nextChildSet = nextFiberChild ? nextFiberChild.sibling : null;
}
if (nextChildSet != null) {
recordResetChildren(nextFiber, nextChildSet);
}
recordResetChildren(nextFiber, nextChildSet);
// We've handled the child order change for this Fiber.
// Since it's included, there's no need to invalidate parent child order.
return false;
@@ -1260,7 +1290,7 @@ export function attach(
function findNativeByFiberID(id: number) {
try {
const fiber = findCurrentFiberUsingSlowPath(idToFiberMap.get(id));
const fiber = findCurrentFiberUsingSlowPathById(id);
if (fiber === null) {
return null;
}
@@ -1550,6 +1580,11 @@ export function attach(
// Find the currently mounted version of this fiber (so we don't show the wrong props and state).
fiber = findCurrentFiberUsingSlowPath(fiber);
if (fiber == null) {
console.warn(`Could not find Fiber with id "${id}"`);
return null;
}
const {
_debugOwner,
_debugSource,
@@ -1558,7 +1593,7 @@ export function attach(
memoizedState,
tag,
type,
} = ((fiber: any): Fiber);
} = fiber;
const usesHooks =
(tag === FunctionComponent ||
@@ -1632,7 +1667,7 @@ export function attach(
displayName: getDataForFiber(owner).displayName || 'Unknown',
id: getFiberID(getPrimaryFiber(owner)),
});
owner = owner._debugOwner;
owner = owner._debugOwner || null;
}
}
@@ -1729,13 +1764,21 @@ export function attach(
}
}
function findCurrentFiberUsingSlowPathById(id: number): Fiber | null {
const fiber = idToFiberMap.get(id);
if (fiber) {
return findCurrentFiberUsingSlowPath(fiber);
}
return null;
}
function setInHook(
id: number,
index: number,
path: Array<string | number>,
value: any
) {
const fiber = findCurrentFiberUsingSlowPath(idToFiberMap.get(id));
const fiber = findCurrentFiberUsingSlowPathById(id);
if (fiber !== null) {
if (typeof overrideHookState === 'function') {
overrideHookState(fiber, index, path, value);
@@ -1744,7 +1787,7 @@ export function attach(
}
function setInProps(id: number, path: Array<string | number>, value: any) {
const fiber = findCurrentFiberUsingSlowPath(idToFiberMap.get(id));
const fiber = findCurrentFiberUsingSlowPathById(id);
if (fiber !== null) {
const instance = fiber.stateNode;
if (instance === null) {
@@ -1759,7 +1802,7 @@ export function attach(
}
function setInState(id: number, path: Array<string | number>, value: any) {
const fiber = findCurrentFiberUsingSlowPath(idToFiberMap.get(id));
const fiber = findCurrentFiberUsingSlowPathById(id);
if (fiber !== null) {
const instance = fiber.stateNode;
setInObject(instance.state, path, value);
@@ -1773,7 +1816,7 @@ export function attach(
// We need to remove the first part of the path (the "value") before continuing.
path = path.slice(1);
const fiber = findCurrentFiberUsingSlowPath(idToFiberMap.get(id));
const fiber = findCurrentFiberUsingSlowPathById(id);
if (fiber !== null) {
const instance = fiber.stateNode;
if (path.length === 0) {
@@ -1940,17 +1983,18 @@ export function attach(
}
const initialTreeBaseDurations = [];
((initialTreeBaseDurationsMap: any): Map<number, number>).forEach(
(treeBaseDuration, id) => {
if (initialTreeBaseDurationsMap != null) {
initialTreeBaseDurationsMap.forEach((treeBaseDuration, id) => {
if (
((initialIDToRootMap: any): Map<number, number>).get(id) === rootID
initialIDToRootMap != null &&
initialIDToRootMap.get(id) === rootID
) {
// We don't need to convert milliseconds to microseconds in this case,
// because the profiling summary is JSON serialized.
initialTreeBaseDurations.push(id, treeBaseDuration);
}
}
);
});
}
return {
commitDurations,
+82 -2
View File
@@ -7,8 +7,88 @@ type BundleType =
| 0 // PROD
| 1; // DEV
// TODO: Better type for Fiber
export type Fiber = Object;
export type WorkTag = number;
export type TypeOfMode = number;
export type SideEffectTag = number;
export type ExpirationTime = number;
export type RefObject = {|
current: any,
|};
export type Source = {
fileName: string,
lineNumber: number,
};
export type HookType =
| 'useState'
| 'useReducer'
| 'useContext'
| 'useRef'
| 'useEffect'
| 'useLayoutEffect'
| 'useCallback'
| 'useMemo'
| 'useImperativeHandle'
| 'useDebugValue';
// Copied modified from https://github.com/facebook/react/blob/v16.9.0-alpha.0/packages/react-reconciler/src/ReactFiber.js
export type Fiber = {|
tag: WorkTag,
key: null | string,
elementType: any,
type: any,
stateNode: any,
return: Fiber | null,
child: Fiber | null,
sibling: Fiber | null,
index: number,
ref: null | (((handle: mixed) => void) & { _stringRef: ?string }) | RefObject,
pendingProps: any, // This type will be more specific once we overload the tag.
memoizedProps: any, // The props used to create the output.
// updateQueue: UpdateQueue<any> | null,
memoizedState: any,
// contextDependencies: ContextDependencyList | null,
mode: TypeOfMode,
effectTag: SideEffectTag,
nextEffect: Fiber | null,
firstEffect: Fiber | null,
lastEffect: Fiber | null,
expirationTime: ExpirationTime,
childExpirationTime: ExpirationTime,
alternate: Fiber | null,
actualDuration?: number,
actualStartTime?: number,
selfBaseDuration?: number,
treeBaseDuration?: number,
_debugID?: number,
_debugSource?: Source | null,
_debugOwner?: Fiber | null,
_debugIsCurrentlyTiming?: boolean,
_debugHookTypes?: Array<HookType> | null,
|};
// TODO: If it's useful for the frontend to know which types of data an Element has
// (e.g. props, state, context, hooks) then we could add a bitmask field for this