Skip to content

Commit 2a879cd

Browse files
authored
[DevTools] Fix broken commit tree builder for initial operations (facebook#35710)
1 parent 9a5996a commit 2a879cd

File tree

5 files changed

+28
-66
lines changed

5 files changed

+28
-66
lines changed

packages/react-devtools-shared/src/backend/fiber/renderer.js

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,7 +1138,7 @@ export function attach(
11381138
// if any passive effects called console.warn / console.error.
11391139
let needsToFlushComponentLogs = false;
11401140
1141-
function bruteForceFlushErrorsAndWarnings() {
1141+
function bruteForceFlushErrorsAndWarnings(root: FiberInstance) {
11421142
// Refresh error/warning count for all mounted unfiltered Fibers.
11431143
let hasChanges = false;
11441144
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
@@ -1156,7 +1156,7 @@ export function attach(
11561156
}
11571157
}
11581158
if (hasChanges) {
1159-
flushPendingEvents();
1159+
flushPendingEvents(root);
11601160
}
11611161
}
11621162
@@ -1183,7 +1183,7 @@ export function attach(
11831183
updateMostRecentlyInspectedElementIfNecessary(devtoolsInstance.id);
11841184
}
11851185
}
1186-
flushPendingEvents();
1186+
flushPendingEvents(null);
11871187
}
11881188
11891189
function clearConsoleLogsHelper(instanceID: number, type: 'error' | 'warn') {
@@ -1211,7 +1211,7 @@ export function attach(
12111211
}
12121212
const changed = recordConsoleLogs(devtoolsInstance, componentLogsEntry);
12131213
if (changed) {
1214-
flushPendingEvents();
1214+
flushPendingEvents(null);
12151215
updateMostRecentlyInspectedElementIfNecessary(devtoolsInstance.id);
12161216
}
12171217
}
@@ -1533,6 +1533,8 @@ export function attach(
15331533
if (isProfiling) {
15341534
// Re-mounting a tree while profiling is in progress might break a lot of assumptions.
15351535
// If necessary, we could support this- but it doesn't seem like a necessary use case.
1536+
// Supporting change of filters while profiling would require a refactor
1537+
// to flush after each root instead of at the end.
15361538
throw Error('Cannot modify filter preferences while profiling');
15371539
}
15381540
@@ -1647,7 +1649,8 @@ export function attach(
16471649
focusedActivityFilter.activityID = focusedActivityID;
16481650
}
16491651
1650-
flushPendingEvents();
1652+
// We're not profiling so it's safe to flush without a specific root.
1653+
flushPendingEvents(null);
16511654
16521655
needsToFlushComponentLogs = false;
16531656
}
@@ -2203,7 +2206,12 @@ export function attach(
22032206
}
22042207
}
22052208
2206-
function flushPendingEvents(): void {
2209+
/**
2210+
* Allowed to flush pending events without a specific root when:
2211+
* - pending operations don't record tree mutations e.g. TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS
2212+
* - not profiling (the commit tree builder requires the root of the mutations)
2213+
*/
2214+
function flushPendingEvents(root: FiberInstance | null): void {
22072215
if (shouldBailoutWithPendingOperations()) {
22082216
// If we aren't profiling, we can just bail out here.
22092217
// No use sending an empty update over the bridge.
@@ -2245,11 +2253,10 @@ export function attach(
22452253
// Which in turn enables fiber props, states, and hooks to be inspected.
22462254
let i = 0;
22472255
operations[i++] = rendererID;
2248-
if (currentRoot === null) {
2249-
// TODO: This is not always safe so this field is probably not needed.
2256+
if (root === null) {
22502257
operations[i++] = -1;
22512258
} else {
2252-
operations[i++] = currentRoot.id;
2259+
operations[i++] = root.id;
22532260
}
22542261
22552262
// Now fill in the string table.
@@ -5746,11 +5753,11 @@ export function attach(
57465753
57475754
mountFiberRecursively(root.current, false);
57485755
5756+
flushPendingEvents(currentRoot);
5757+
57495758
currentRoot = (null: any);
57505759
});
57515760
5752-
flushPendingEvents();
5753-
57545761
needsToFlushComponentLogs = false;
57555762
}
57565763
}
@@ -5760,7 +5767,7 @@ export function attach(
57605767
// safe to stop calling it from Fiber.
57615768
}
57625769
5763-
function handlePostCommitFiberRoot(root: any) {
5770+
function handlePostCommitFiberRoot(root: FiberRoot) {
57645771
if (isProfiling && rootSupportsProfiling(root)) {
57655772
if (currentCommitProfilingMetadata !== null) {
57665773
const {effectDuration, passiveEffectDuration} =
@@ -5774,12 +5781,18 @@ export function attach(
57745781
}
57755782
57765783
if (needsToFlushComponentLogs) {
5784+
const rootInstance = rootToFiberInstanceMap.get(root);
5785+
if (rootInstance === undefined) {
5786+
throw new Error(
5787+
'Should have a root instance for a committed root. This is a bug in React DevTools.',
5788+
);
5789+
}
57775790
// We received new logs after commit. I.e. in a passive effect. We need to
57785791
// traverse the tree to find the affected ones. If we just moved the whole
57795792
// tree traversal from handleCommitFiberRoot to handlePostCommitFiberRoot
57805793
// this wouldn't be needed. For now we just brute force check all instances.
57815794
// This is not that common of a case.
5782-
bruteForceFlushErrorsAndWarnings();
5795+
bruteForceFlushErrorsAndWarnings(rootInstance);
57835796
}
57845797
}
57855798
@@ -5876,7 +5889,7 @@ export function attach(
58765889
}
58775890
58785891
// We're done here.
5879-
flushPendingEvents();
5892+
flushPendingEvents(currentRoot);
58805893
58815894
needsToFlushComponentLogs = false;
58825895

packages/react-devtools-shared/src/constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const TREE_OPERATION_REMOVE = 2;
2222
export const TREE_OPERATION_REORDER_CHILDREN = 3;
2323
export const TREE_OPERATION_UPDATE_TREE_BASE_DURATION = 4;
2424
export const TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS = 5;
25-
export const TREE_OPERATION_REMOVE_ROOT = 6;
25+
// Removed `TREE_OPERATION_REMOVE_ROOT`
2626
export const TREE_OPERATION_SET_SUBTREE_MODE = 7;
2727
export const SUSPENSE_TREE_OPERATION_ADD = 8;
2828
export const SUSPENSE_TREE_OPERATION_REMOVE = 9;

packages/react-devtools-shared/src/devtools/store.js

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
PROFILING_FLAG_PERFORMANCE_TRACKS_SUPPORT,
1717
TREE_OPERATION_ADD,
1818
TREE_OPERATION_REMOVE,
19-
TREE_OPERATION_REMOVE_ROOT,
2019
TREE_OPERATION_REORDER_CHILDREN,
2120
TREE_OPERATION_SET_SUBTREE_MODE,
2221
TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS,
@@ -1644,45 +1643,6 @@ export default class Store extends EventEmitter<{
16441643

16451644
break;
16461645
}
1647-
case TREE_OPERATION_REMOVE_ROOT: {
1648-
i += 1;
1649-
1650-
const id = operations[1];
1651-
1652-
if (__DEBUG__) {
1653-
debug(`Remove root ${id}`);
1654-
}
1655-
1656-
const recursivelyDeleteElements = (elementID: number) => {
1657-
const element = this._idToElement.get(elementID);
1658-
this._idToElement.delete(elementID);
1659-
if (element) {
1660-
// Mostly for Flow's sake
1661-
for (let index = 0; index < element.children.length; index++) {
1662-
recursivelyDeleteElements(element.children[index]);
1663-
}
1664-
}
1665-
};
1666-
1667-
const root = this._idToElement.get(id);
1668-
if (root === undefined) {
1669-
this._throwAndEmitError(
1670-
Error(
1671-
`Cannot remove root "${id}": no matching node was found in the Store.`,
1672-
),
1673-
);
1674-
1675-
break;
1676-
}
1677-
1678-
recursivelyDeleteElements(id);
1679-
1680-
this._rootIDToCapabilities.delete(id);
1681-
this._rootIDToRendererID.delete(id);
1682-
this._roots = this._roots.filter(rootID => rootID !== id);
1683-
this._weightAcrossRoots -= root.weight;
1684-
break;
1685-
}
16861646
case TREE_OPERATION_REORDER_CHILDREN: {
16871647
const id = operations[i + 1];
16881648
const numChildren = operations[i + 2];

packages/react-devtools-shared/src/devtools/views/Profiler/CommitTreeBuilder.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
__DEBUG__,
1212
TREE_OPERATION_ADD,
1313
TREE_OPERATION_REMOVE,
14-
TREE_OPERATION_REMOVE_ROOT,
1514
TREE_OPERATION_REORDER_CHILDREN,
1615
TREE_OPERATION_SET_SUBTREE_MODE,
1716
TREE_OPERATION_UPDATE_TREE_BASE_DURATION,
@@ -313,9 +312,6 @@ function updateTree(
313312
}
314313
break;
315314
}
316-
case TREE_OPERATION_REMOVE_ROOT: {
317-
throw Error('Operation REMOVE_ROOT is not supported while profiling.');
318-
}
319315
case TREE_OPERATION_REORDER_CHILDREN: {
320316
id = ((operations[i + 1]: any): number);
321317
const numChildren = ((operations[i + 2]: any): number);

packages/react-devtools-shared/src/utils.js

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
import {
2929
TREE_OPERATION_ADD,
3030
TREE_OPERATION_REMOVE,
31-
TREE_OPERATION_REMOVE_ROOT,
3231
TREE_OPERATION_REORDER_CHILDREN,
3332
TREE_OPERATION_SET_SUBTREE_MODE,
3433
TREE_OPERATION_UPDATE_ERRORS_OR_WARNINGS,
@@ -295,12 +294,6 @@ export function printOperationsArray(operations: Array<number>) {
295294
}
296295
break;
297296
}
298-
case TREE_OPERATION_REMOVE_ROOT: {
299-
i += 1;
300-
301-
logs.push(`Remove root ${rootID}`);
302-
break;
303-
}
304297
case TREE_OPERATION_SET_SUBTREE_MODE: {
305298
const id = operations[i + 1];
306299
const mode = operations[i + 2];

0 commit comments

Comments
 (0)