@samitouri / QOS-React / commits / 80597a2377

Use a heuristic for locating roots

Dan Abramov committed Apr 24, 2019 at 21:32 UTC 80597a23779fad309a74f79d390ccd3c631d5330
1 file changed +81 -20
src/backend/renderer.js
+81 -20
@@ -539,11 +539,6 @@ export function attach(
539 // When a mount or update is in progress, this value tracks the root that is being operated on.
540 let currentRootID: number = -1;
541
542 - // Track the order in which roots were added.
543 - // We will use it to disambiguate roots when restoring selection between reloads.
544 - let nextRootIndex = 0;
545 - const rootInsertionOrder: Map<number, number> = new Map();
546 -
542 function getFiberID(primaryFiber: Fiber): number {
543 if (!fiberToIDMap.has(primaryFiber)) {
544 const id = getUID();
@@ -1148,7 +1143,7 @@ export function attach(
1143 // If we have not been profiling, then we can just walk the tree and build up its current state as-is.
1144 hook.getFiberRoots(rendererID).forEach(root => {
1145 currentRootID = getFiberID(getPrimaryFiber(root.current));
1151 - rootInsertionOrder.set(currentRootID, nextRootIndex++);
1146 + setRootPseudoKey(currentRootID, root.current);
1147
1148 if (isProfiling) {
1149 // If profiling is active, store commit time and duration, and the current interactions.
@@ -1217,19 +1212,19 @@ export function attach(
1212 current.memoizedState != null && current.memoizedState.element != null;
1213 if (!wasMounted && isMounted) {
1214 // Mount a new root.
1220 - rootInsertionOrder.set(currentRootID, nextRootIndex++);
1215 + setRootPseudoKey(currentRootID, current);
1216 mountFiberRecursively(current, null);
1217 } else if (wasMounted && isMounted) {
1218 // Update an existing root.
1219 updateFiberRecursively(current, alternate, null);
1220 } else if (wasMounted && !isMounted) {
1221 // Unmount an existing root.
1227 - rootInsertionOrder.delete(currentRootID);
1222 + removeRootPseudoKey(currentRootID);
1223 recordUnmount(current, false);
1224 }
1225 } else {
1226 // Mount a new root.
1232 - rootInsertionOrder.set(currentRootID, nextRootIndex++);
1227 + setRootPseudoKey(currentRootID, current);
1228 mountFiberRecursively(current, null);
1229 }
1230
@@ -2099,18 +2094,84 @@ export function attach(
2094 mightBeOnTrackedPath = mightSiblingsBeOnTrackedPath;
2095 }
2096
2102 - function getPathFrame(fiber: Fiber): PathFrame {
2103 - const { displayName, key } = getDataForFiber(fiber);
2104 - let index = fiber.index;
2105 - if (fiber.tag === HostRoot) {
2106 - // Roots don't have a real index.
2107 - // Instead, we'll use the order in which it mounted.
2108 - const id = getFiberID(getPrimaryFiber(fiber));
2109 - const order = rootInsertionOrder.get(id);
2110 - if (typeof order !== 'number') {
2111 - throw new Error('Expected mounted root to have known insertion order.');
2097 + // Roots don't have a real persistent identity.
2098 + // A root's "pseudo key" is "childDisplayName:indexWithThatName".
2099 + // For example, "App:0" or, in case of similar roots, "Story:0", "Story:1", etc.
2100 + // We will use this to try to disambiguate roots when restoring selection between reloads.
2101 + const rootPseudoKeys: Map<number, string> = new Map();
2102 + const rootDisplayNameCounter: Map<string, number> = new Map();
2103 +
2104 + function setRootPseudoKey(id: number, fiber: Fiber) {
2105 + let preferredDisplayName = null;
2106 + let fallbackDisplayName = null;
2107 + let child = fiber.child;
2108 + // Go at most three levels deep into direct children
2109 + // while searching for a child that has a displayName.
2110 + for (let i = 0; i < 3; i++) {
2111 + if (child === null) {
2112 + break;
2113 + }
2114 + const displayName = getDataForFiber(child).displayName;
2115 + if (displayName !== null) {
2116 + // Prefer display names that we get from user-defined components.
2117 + // We want to avoid using e.g. 'Suspense' unless we find nothing else.
2118 + if (typeof child.type === 'function') {
2119 + // There's a few user-defined tags, but we'll prefer the ones
2120 + // that are usually explicitly named (function or class components).
2121 + preferredDisplayName = displayName;
2122 + } else if (fallbackDisplayName === null) {
2123 + fallbackDisplayName = displayName;
2124 + }
2125 + }
2126 + if (preferredDisplayName !== null) {
2127 + break;
2128 }
2113 - index = order;
2129 + child = child.child;
2130 + }
2131 + const name = preferredDisplayName || fallbackDisplayName || 'Unknown';
2132 + const counter = rootDisplayNameCounter.get(name) || 0;
2133 + rootDisplayNameCounter.set(name, counter + 1);
2134 + const pseudoKey = `${name}:${counter}`;
2135 + rootPseudoKeys.set(id, pseudoKey);
2136 + }
2137 +
2138 + function removeRootPseudoKey(id: number) {
2139 + const pseudoKey = rootPseudoKeys.get(id);
2140 + if (pseudoKey === undefined) {
2141 + throw new Error('Expected root pseudo key to be known.');
2142 + }
2143 + const name = pseudoKey.substring(0, pseudoKey.lastIndexOf(':'));
2144 + const counter = rootDisplayNameCounter.get(name);
2145 + if (counter === undefined) {
2146 + throw new Error('Expected counter to be known.');
2147 + }
2148 + if (counter > 1) {
2149 + rootDisplayNameCounter.set(name, counter - 1);
2150 + } else {
2151 + rootDisplayNameCounter.delete(name);
2152 + }
2153 + rootPseudoKeys.delete(id);
2154 + }
2155 +
2156 + function getPathFrame(fiber: Fiber): PathFrame {
2157 + let { displayName, key } = getDataForFiber(fiber);
2158 + const index = fiber.index;
2159 + switch (fiber.tag) {
2160 + case HostRoot:
2161 + // Roots don't have a real displayName, index, or key.
2162 + // Instead, we'll use the pseudo key (childDisplayName:indexWithThatName).
2163 + const id = getFiberID(getPrimaryFiber(fiber));
2164 + const pseudoKey = rootPseudoKeys.get(id);
2165 + if (pseudoKey === undefined) {
2166 + throw new Error('Expected mounted root to have known pseudo key.');
2167 + }
2168 + displayName = pseudoKey;
2169 + break;
2170 + case HostComponent:
2171 + displayName = fiber.type;
2172 + break;
2173 + default:
2174 + break;
2175 }
2176 return {
2177 displayName,