Skip to content

Commit 5e83d9a

Browse files
authored
feat[react-devtools]: add settings to global hook object (#30564)
Right now we are patching console 2 times: when hook is installed (before page is loaded) and when backend is connected. Because of this, even if user had `appendComponentStack` setting enabled, all emitted error and warning logs are not going to have component stacks appended. They also won't have component stacks appended retroactively when user opens browser DevTools (this is when frontend is initialized and connects to backend). This behavior adds potential race conditions with LogBox in React Native, and also unpredictable to the user, because in order to get component stacks logged you have to open browser DevTools, but by the time you do it, error or warning log was already emitted. To solve this, we are going to only patch console in the hook object, because it is guaranteed to load even before React. Settings are going to be synchronized with the hook via Bridge, and React DevTools Backend Host (React Native or browser extension shell) will be responsible for persisting these settings across the session, this is going to be implemented in a separate PR.
1 parent 5dcb009 commit 5e83d9a

File tree

5 files changed

+55
-26
lines changed

5 files changed

+55
-26
lines changed

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

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,9 @@ import type {
3737
RendererID,
3838
RendererInterface,
3939
ConsolePatchSettings,
40+
DevToolsHookSettings,
4041
} from './types';
41-
import type {
42-
ComponentFilter,
43-
BrowserTheme,
44-
} from 'react-devtools-shared/src/frontend/types';
42+
import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types';
4543
import {isSynchronousXHRSupported, isReactNativeEnvironment} from './utils';
4644

4745
const debug = (methodName: string, ...args: Array<string>) => {
@@ -153,6 +151,7 @@ export default class Agent extends EventEmitter<{
153151
drawTraceUpdates: [Array<HostInstance>],
154152
disableTraceUpdates: [],
155153
getIfHasUnsupportedRendererVersion: [],
154+
updateHookSettings: [DevToolsHookSettings],
156155
}> {
157156
_bridge: BackendBridge;
158157
_isProfiling: boolean = false;
@@ -805,30 +804,22 @@ export default class Agent extends EventEmitter<{
805804
}
806805
};
807806

808-
updateConsolePatchSettings: ({
809-
appendComponentStack: boolean,
810-
breakOnConsoleErrors: boolean,
811-
browserTheme: BrowserTheme,
812-
hideConsoleLogsInStrictMode: boolean,
813-
showInlineWarningsAndErrors: boolean,
814-
}) => void = ({
815-
appendComponentStack,
816-
breakOnConsoleErrors,
817-
showInlineWarningsAndErrors,
818-
hideConsoleLogsInStrictMode,
819-
browserTheme,
820-
}: ConsolePatchSettings) => {
807+
updateConsolePatchSettings: (
808+
settings: $ReadOnly<ConsolePatchSettings>,
809+
) => void = settings => {
810+
// Propagate the settings, so Backend can subscribe to it and modify hook
811+
this.emit('updateHookSettings', {
812+
appendComponentStack: settings.appendComponentStack,
813+
breakOnConsoleErrors: settings.breakOnConsoleErrors,
814+
showInlineWarningsAndErrors: settings.showInlineWarningsAndErrors,
815+
hideConsoleLogsInStrictMode: settings.hideConsoleLogsInStrictMode,
816+
});
817+
821818
// If the frontend preferences have changed,
822819
// or in the case of React Native- if the backend is just finding out the preferences-
823820
// then reinstall the console overrides.
824821
// It's safe to call `patchConsole` multiple times.
825-
patchConsole({
826-
appendComponentStack,
827-
breakOnConsoleErrors,
828-
showInlineWarningsAndErrors,
829-
hideConsoleLogsInStrictMode,
830-
browserTheme,
831-
});
822+
patchConsole(settings);
832823
};
833824

834825
updateComponentFilters: (componentFilters: Array<ComponentFilter>) => void =

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export function patch({
135135
showInlineWarningsAndErrors,
136136
hideConsoleLogsInStrictMode,
137137
browserTheme,
138-
}: ConsolePatchSettings): void {
138+
}: $ReadOnly<ConsolePatchSettings>): void {
139139
// Settings may change after we've patched the console.
140140
// Using a shared ref allows the patch function to read the latest values.
141141
consoleSettingsRef.appendComponentStack = appendComponentStack;

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ export function initBackend(
8383
agent.removeListener('shutdown', onAgentShutdown);
8484
});
8585

86+
agent.addListener('updateHookSettings', settings => {
87+
hook.settings = settings;
88+
});
89+
8690
return () => {
8791
subs.forEach(fn => fn());
8892
};

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ export type DevToolsHook = {
527527
// Testing
528528
dangerous_setTargetConsoleForTesting?: (fakeConsole: Object) => void,
529529

530+
settings?: DevToolsHookSettings,
530531
...
531532
};
532533

@@ -537,3 +538,10 @@ export type ConsolePatchSettings = {
537538
hideConsoleLogsInStrictMode: boolean,
538539
browserTheme: BrowserTheme,
539540
};
541+
542+
export type DevToolsHookSettings = {
543+
appendComponentStack: boolean,
544+
breakOnConsoleErrors: boolean,
545+
showInlineWarningsAndErrors: boolean,
546+
hideConsoleLogsInStrictMode: boolean,
547+
};

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

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
RendererID,
1616
RendererInterface,
1717
DevToolsBackend,
18+
DevToolsHookSettings,
1819
} from './backend/types';
1920

2021
import {
@@ -25,7 +26,12 @@ import attachRenderer from './attachRenderer';
2526

2627
declare var window: any;
2728

28-
export function installHook(target: any): DevToolsHook | null {
29+
export function installHook(
30+
target: any,
31+
maybeSettingsOrSettingsPromise?:
32+
| DevToolsHookSettings
33+
| Promise<DevToolsHookSettings>,
34+
): DevToolsHook | null {
2935
if (target.hasOwnProperty('__REACT_DEVTOOLS_GLOBAL_HOOK__')) {
3036
return null;
3137
}
@@ -566,6 +572,26 @@ export function installHook(target: any): DevToolsHook | null {
566572
registerInternalModuleStop,
567573
};
568574

575+
if (maybeSettingsOrSettingsPromise == null) {
576+
// Set default settings
577+
hook.settings = {
578+
appendComponentStack: true,
579+
breakOnConsoleErrors: false,
580+
showInlineWarningsAndErrors: true,
581+
hideConsoleLogsInStrictMode: false,
582+
};
583+
} else {
584+
Promise.resolve(maybeSettingsOrSettingsPromise)
585+
.then(settings => {
586+
hook.settings = settings;
587+
})
588+
.catch(() => {
589+
targetConsole.error(
590+
"React DevTools failed to get Console Patching settings. Console won't be patched and some console features will not work.",
591+
);
592+
});
593+
}
594+
569595
if (__TEST__) {
570596
hook.dangerous_setTargetConsoleForTesting =
571597
dangerous_setTargetConsoleForTesting;

0 commit comments

Comments
 (0)