Skip to content

Commit 5d3d71b

Browse files
author
Brian Vaughn
authored
Partial fix DevTools Profiler ''Could not find node…'' error (#17759)
The Profiler stores: 1. A snapshot of the React tree when profiling started 2. The operations array for each commit 3. Profiling metadata (e.g. durations, what changed, etc) for each commit It uses this information (snapshot + operations diff) to reconstruct the state of the application for a given commit as it's viewed in the Profiler UI. Because of this, it's very important that the operations and metadata arrays align. If they don't align, the profiler will be unable to correctly reconstruct the tree, and it will likely throw errors (like 'Could not find node…') #16446 tracks a long-standing bug where these two arrays get misaligned. I am still not entirely sure what causes this bug, but with PR #17253, I exacerbated things by introducing another potential way for it to happen. This PR addresses the regression at least (and adds test coverage for it). I will follow up this afternoon on the original #16446 issue. I think I may have a lead on what's happening at least, if not exactly an idea of how to reproduce it.
1 parent cca994c commit 5d3d71b

File tree

2 files changed

+25
-3
lines changed

2 files changed

+25
-3
lines changed

packages/react-devtools-shared/src/__tests__/profilerStore-test.js

+1
Original file line numberDiff line numberDiff line change
@@ -118,5 +118,6 @@ describe('ProfilerStore', () => {
118118
const root = store.roots[0];
119119
const data = store.profilerStore.getDataForRoot(root);
120120
expect(data.commitData).toHaveLength(1);
121+
expect(data.operations).toHaveLength(1);
121122
});
122123
});

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

+24-3
Original file line numberDiff line numberDiff line change
@@ -1021,12 +1021,32 @@ export function attach(
10211021
pendingSimulatedUnmountedIDs.length === 0 &&
10221022
pendingUnmountedRootID === null
10231023
) {
1024-
// If we're currently profiling, send an "operations" method even if there are no mutations to the tree.
1025-
// The frontend needs this no-op info to know how to reconstruct the tree for each commit,
1026-
// even if a particular commit didn't change the shape of the tree.
1024+
// If we aren't profiling, we can just bail out here.
1025+
// No use sending an empty update over the bridge.
10271026
if (!isProfiling) {
10281027
return;
10291028
}
1029+
1030+
const current = root.current;
1031+
const alternate = current.alternate;
1032+
1033+
// Certain types of updates bail out at the root without doing any actual render work.
1034+
// React should probably not call the DevTools commit hook in this case,
1035+
// but if it does- we can detect it and filter them out from the profiler.
1036+
// NOTE: Keep this logic in sync with the one in handleCommitFiberRoot()
1037+
const didBailoutAtRoot =
1038+
alternate !== null &&
1039+
alternate.expirationTime === 0 &&
1040+
alternate.childExpirationTime === 0;
1041+
1042+
// The Profiler stores metadata for each commit and reconstructs the app tree per commit using:
1043+
// (1) an initial tree snapshot and
1044+
// (2) the operations array for each commit
1045+
// Because of this, it's important that the operations and metadata arrays align,
1046+
// So the logic that skips metadata for bailout commits should also apply to filter operations.
1047+
if (didBailoutAtRoot) {
1048+
return;
1049+
}
10301050
}
10311051

10321052
const numUnmountIDs =
@@ -1758,6 +1778,7 @@ export function attach(
17581778
// Certain types of updates bail out at the root without doing any actual render work.
17591779
// React should probably not call the DevTools commit hook in this case,
17601780
// but if it does- we can detect it and filter them out from the profiler.
1781+
// NOTE: Keep this logic in sync with the one in flushPendingEvents()
17611782
const didBailoutAtRoot =
17621783
alternate !== null &&
17631784
alternate.expirationTime === 0 &&

0 commit comments

Comments
 (0)