Skip to content

Commit a86afe8

Browse files
authored
feat: expose installHook with settings argument from react-devtools-core/backend (#30987)
Stacked on #30986. Previously, we would call `installHook` at a top level of the JavaScript module. Because of this, having `require` statement for `react-devtools-core` package was enough to initialize the React DevTools global hook on the `window`. Now, the Hook can actually receive an argument - initial user settings for console patching. We expose this as a function `initialize`, which can be used by third parties (including React Native) to provide the persisted settings. The README was also updated to reflect the changes.
1 parent e72127a commit a86afe8

File tree

2 files changed

+62
-23
lines changed

2 files changed

+62
-23
lines changed

packages/react-devtools-core/README.md

+29-18
Original file line numberDiff line numberDiff line change
@@ -14,35 +14,46 @@ If you are building a non-browser-based React renderer, you can use the backend
1414

1515
```js
1616
if (process.env.NODE_ENV !== 'production') {
17-
const { connectToDevTools } = require("react-devtools-core");
17+
const { initialize, connectToDevTools } = require("react-devtools-core");
1818

19+
initialize(settings);
1920
// Must be called before packages like react or react-native are imported
20-
connectToDevTools({
21-
...config
22-
});
21+
connectToDevTools({...config});
2322
}
2423
```
2524

2625
> **NOTE** that this API (`connectToDevTools`) must be (1) run in the same context as React and (2) must be called before React packages are imported (e.g. `react`, `react-dom`, `react-native`).
2726
27+
### `initialize` arguments
28+
| Argument | Description |
29+
|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
30+
| `settings` | Optional. If not specified, or received as null, then default settings are used. Can be plain object or a Promise that resolves with the [plain settings object](#Settings). If Promise rejects, the console will not be patched and some console features from React DevTools will not work. |
31+
32+
#### `Settings`
33+
| Spec | Default value |
34+
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
35+
| <pre>{<br> appendComponentStack: boolean,<br> breakOnConsoleErrors: boolean,<br> showInlineWarningsAndErrors: boolean,<br> hideConsoleLogsInStrictMode: boolean<br>}</pre> | <pre>{<br> appendComponentStack: true,<br> breakOnConsoleErrors: false,<br> showInlineWarningsAndErrors: true,<br> hideConsoleLogsInStrictMode: false<br>}</pre> |
36+
2837
### `connectToDevTools` options
29-
| Prop | Default | Description |
30-
|---|---|---|
31-
| `host` | `"localhost"` | Socket connection to frontend should use this host. |
32-
| `isAppActive` | | (Optional) function that returns true/false, telling DevTools when it's ready to connect to React. |
33-
| `port` | `8097` | Socket connection to frontend should use this port. |
34-
| `resolveRNStyle` | | (Optional) function that accepts a key (number) and returns a style (object); used by React Native. |
35-
| `retryConnectionDelay` | `200` | Delay (ms) to wait between retrying a failed Websocket connection |
36-
| `useHttps` | `false` | Socket connection to frontend should use secure protocol (wss). |
37-
| `websocket` | | Custom `WebSocket` connection to frontend; overrides `host` and `port` settings. |
38+
| Prop | Default | Description |
39+
|------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------|
40+
| `host` | `"localhost"` | Socket connection to frontend should use this host. |
41+
| `isAppActive` | | (Optional) function that returns true/false, telling DevTools when it's ready to connect to React. |
42+
| `port` | `8097` | Socket connection to frontend should use this port. |
43+
| `resolveRNStyle` | | (Optional) function that accepts a key (number) and returns a style (object); used by React Native. |
44+
| `retryConnectionDelay` | `200` | Delay (ms) to wait between retrying a failed Websocket connection |
45+
| `useHttps` | `false` | Socket connection to frontend should use secure protocol (wss). |
46+
| `websocket` | | Custom `WebSocket` connection to frontend; overrides `host` and `port` settings. |
47+
| `onSettingsUpdated` | | A callback that will be called when the user updates the settings in the UI. You can use it for persisting user settings. | |
3848

3949

4050
### `connectWithCustomMessagingProtocol` options
41-
| Prop | Description |
42-
|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
43-
| `onSubscribe` | Function, which receives listener (function, with a single argument) as an argument. Called when backend subscribes to messages from the other end (frontend). |
44-
| `onUnsubscribe` | Function, which receives listener (function) as an argument. Called when backend unsubscribes to messages from the other end (frontend). |
45-
| `onMessage` | Function, which receives 2 arguments: event (string) and payload (any). Called when backend emits a message, which should be sent to the frontend. |
51+
| Prop | Description |
52+
|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|
53+
| `onSubscribe` | Function, which receives listener (function, with a single argument) as an argument. Called when backend subscribes to messages from the other end (frontend). |
54+
| `onUnsubscribe` | Function, which receives listener (function) as an argument. Called when backend unsubscribes to messages from the other end (frontend). |
55+
| `onMessage` | Function, which receives 2 arguments: event (string) and payload (any). Called when backend emits a message, which should be sent to the frontend. |
56+
| `onSettingsUpdated` | A callback that will be called when the user updates the settings in the UI. You can use it for persisting user settings. |
4657

4758
Unlike `connectToDevTools`, `connectWithCustomMessagingProtocol` returns a callback, which can be used for unsubscribing the backend from the global DevTools hook.
4859

packages/react-devtools-core/src/backend.js

+33-5
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import type {
2020
ComponentFilter,
2121
Wall,
2222
} from 'react-devtools-shared/src/frontend/types';
23-
import type {DevToolsHook} from 'react-devtools-shared/src/backend/types';
23+
import type {
24+
DevToolsHook,
25+
DevToolsHookSettings,
26+
} from 'react-devtools-shared/src/backend/types';
2427
import type {ResolveNativeStyle} from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor';
2528

2629
type ConnectOptions = {
@@ -32,12 +35,9 @@ type ConnectOptions = {
3235
retryConnectionDelay?: number,
3336
isAppActive?: () => boolean,
3437
websocket?: ?WebSocket,
38+
onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
3539
};
3640

37-
installHook(window);
38-
39-
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
40-
4141
let savedComponentFilters: Array<ComponentFilter> =
4242
getDefaultComponentFilters();
4343

@@ -52,11 +52,21 @@ function debug(methodName: string, ...args: Array<mixed>) {
5252
}
5353
}
5454

55+
export function initialize(
56+
maybeSettingsOrSettingsPromise?:
57+
| DevToolsHookSettings
58+
| Promise<DevToolsHookSettings>,
59+
) {
60+
installHook(window, maybeSettingsOrSettingsPromise);
61+
}
62+
5563
export function connectToDevTools(options: ?ConnectOptions) {
64+
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
5665
if (hook == null) {
5766
// DevTools didn't get injected into this page (maybe b'c of the contentType).
5867
return;
5968
}
69+
6070
const {
6171
host = 'localhost',
6272
nativeStyleEditorValidAttributes,
@@ -66,6 +76,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
6676
resolveRNStyle = (null: $FlowFixMe),
6777
retryConnectionDelay = 2000,
6878
isAppActive = () => true,
79+
onSettingsUpdated,
6980
} = options || {};
7081

7182
const protocol = useHttps ? 'wss' : 'ws';
@@ -160,7 +171,14 @@ export function connectToDevTools(options: ?ConnectOptions) {
160171
// TODO (npm-packages) Warn if "isBackendStorageAPISupported"
161172
// $FlowFixMe[incompatible-call] found when upgrading Flow
162173
const agent = new Agent(bridge);
174+
if (onSettingsUpdated != null) {
175+
agent.addListener('updateHookSettings', onSettingsUpdated);
176+
}
163177
agent.addListener('shutdown', () => {
178+
if (onSettingsUpdated != null) {
179+
agent.removeListener('updateHookSettings', onSettingsUpdated);
180+
}
181+
164182
// If we received 'shutdown' from `agent`, we assume the `bridge` is already shutting down,
165183
// and that caused the 'shutdown' event on the `agent`, so we don't need to call `bridge.shutdown()` here.
166184
hook.emit('shutdown');
@@ -290,6 +308,7 @@ type ConnectWithCustomMessagingOptions = {
290308
onMessage: (event: string, payload: any) => void,
291309
nativeStyleEditorValidAttributes?: $ReadOnlyArray<string>,
292310
resolveRNStyle?: ResolveNativeStyle,
311+
onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
293312
};
294313

295314
export function connectWithCustomMessagingProtocol({
@@ -298,7 +317,9 @@ export function connectWithCustomMessagingProtocol({
298317
onMessage,
299318
nativeStyleEditorValidAttributes,
300319
resolveRNStyle,
320+
onSettingsUpdated,
301321
}: ConnectWithCustomMessagingOptions): Function {
322+
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
302323
if (hook == null) {
303324
// DevTools didn't get injected into this page (maybe b'c of the contentType).
304325
return;
@@ -334,7 +355,14 @@ export function connectWithCustomMessagingProtocol({
334355
}
335356

336357
const agent = new Agent(bridge);
358+
if (onSettingsUpdated != null) {
359+
agent.addListener('updateHookSettings', onSettingsUpdated);
360+
}
337361
agent.addListener('shutdown', () => {
362+
if (onSettingsUpdated != null) {
363+
agent.removeListener('updateHookSettings', onSettingsUpdated);
364+
}
365+
338366
// If we received 'shutdown' from `agent`, we assume the `bridge` is already shutting down,
339367
// and that caused the 'shutdown' event on the `agent`, so we don't need to call `bridge.shutdown()` here.
340368
hook.emit('shutdown');

0 commit comments

Comments
 (0)