[DevTools] Reset forced states when changing component filters (#34929)

This commit is contained in:
Sebastian "Sebbie" Silbermann 2025-10-31 12:57:11 +01:00 committed by GitHub
parent 6fb7754494
commit c9ddee7e36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 128 additions and 0 deletions

View File

@ -116,6 +116,16 @@ function shouldIgnoreConsoleErrorOrWarn(args) {
return false;
}
const maybeError = args[1];
if (
maybeError !== null &&
typeof maybeError === 'object' &&
maybeError.message === 'Simulated error coming from DevTools'
) {
// Error from forcing an error boundary.
return true;
}
return global._ignoredErrorOrWarningMessages.some(errorOrWarningMessage => {
return firstArg.indexOf(errorOrWarningMessage) !== -1;
});

View File

@ -18,12 +18,14 @@ import {
describe('Store component filters', () => {
let React;
let Types;
let agent;
let bridge: FrontendBridge;
let store: Store;
let utils;
let actAsync;
beforeEach(() => {
agent = global.agent;
bridge = global.bridge;
store = global.store;
store.collapseNodesByDefault = false;
@ -740,4 +742,80 @@ describe('Store component filters', () => {
`);
});
});
// @reactVersion >= 16.6
it('resets forced error and fallback states when filters are changed', async () => {
store.componentFilters = [];
class ErrorBoundary extends React.Component {
state = {hasError: false};
static getDerivedStateFromError() {
return {hasError: true};
}
render() {
if (this.state.hasError) {
return <div key="did-error" />;
}
return this.props.children;
}
}
function App() {
return (
<>
<React.Suspense fallback={<div key="loading" />}>
<div key="suspense-content" />
</React.Suspense>
<ErrorBoundary>
<div key="error-content" />
</ErrorBoundary>
</>
);
}
await actAsync(async () => {
render(<App />);
});
const rendererID = utils.getRendererID();
await actAsync(() => {
agent.overrideSuspense({
id: store.getElementIDAtIndex(2),
rendererID,
forceFallback: true,
});
agent.overrideError({
id: store.getElementIDAtIndex(4),
rendererID,
forceError: true,
});
});
expect(store).toMatchInlineSnapshot(`
[root]
<App>
<Suspense>
<div key="loading">
<ErrorBoundary>
<div key="did-error">
[suspense-root] rects={[]}
<Suspense name="App" rects={[]}>
`);
await actAsync(() => {
store.componentFilters = [
utils.createElementTypeFilter(Types.ElementTypeFunction, true),
];
});
expect(store).toMatchInlineSnapshot(`
[root]
<Suspense>
<div key="suspense-content">
<ErrorBoundary>
<div key="error-content">
[suspense-root] rects={[]}
<Suspense name="Unknown" rects={[]}>
`);
});
});

View File

@ -1512,6 +1512,11 @@ export function attach(
throw Error('Cannot modify filter preferences while profiling');
}
const previousForcedFallbacks =
forceFallbackForFibers.size > 0 ? new Set(forceFallbackForFibers) : null;
const previousForcedErrors =
forceErrorForFibers.size > 0 ? new Map(forceErrorForFibers) : null;
// Recursively unmount all roots.
hook.getFiberRoots(rendererID).forEach(root => {
const rootInstance = rootToFiberInstanceMap.get(root);
@ -1532,6 +1537,41 @@ export function attach(
// Reset pseudo counters so that new path selections will be persisted.
rootDisplayNameCounter.clear();
// We just cleared all the forced states. Schedule updates on the affected Fibers
// so that we get their initial states again according to the new filters.
if (typeof scheduleUpdate === 'function') {
if (previousForcedFallbacks !== null) {
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
for (const fiber of previousForcedFallbacks) {
if (typeof scheduleRetry === 'function') {
scheduleRetry(fiber);
} else {
scheduleUpdate(fiber);
}
}
}
if (
previousForcedErrors !== null &&
typeof setErrorHandler === 'function'
) {
// Unlike for Suspense, disabling the forced error state requires setting
// the status to false first. `shouldErrorFiberAccordingToMap` will clear
// the Fibers later.
setErrorHandler(shouldErrorFiberAccordingToMap);
// eslint-disable-next-line no-for-of-loops/no-for-of-loops
for (const [fiber, shouldError] of previousForcedErrors) {
forceErrorForFibers.set(fiber, false);
if (shouldError) {
if (typeof scheduleRetry === 'function') {
scheduleRetry(fiber);
} else {
scheduleUpdate(fiber);
}
}
}
}
}
// Recursively re-mount all roots with new filter criteria applied.
hook.getFiberRoots(rendererID).forEach(root => {
const current = root.current;