diff --git a/packages/react-devtools-core/README.md b/packages/react-devtools-core/README.md index b0f0e98191b35..f3487fefba75e 100644 --- a/packages/react-devtools-core/README.md +++ b/packages/react-devtools-core/README.md @@ -14,35 +14,46 @@ If you are building a non-browser-based React renderer, you can use the backend ```js if (process.env.NODE_ENV !== 'production') { - const { connectToDevTools } = require("react-devtools-core"); + const { initialize, connectToDevTools } = require("react-devtools-core"); + initialize(settings); // Must be called before packages like react or react-native are imported - connectToDevTools({ - ...config - }); + connectToDevTools({...config}); } ``` > **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`). +### `initialize` arguments +| Argument | Description | +|------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `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. | + +#### `Settings` +| Spec | Default value | +|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|
{
appendComponentStack: boolean,
breakOnConsoleErrors: boolean,
showInlineWarningsAndErrors: boolean,
hideConsoleLogsInStrictMode: boolean
}
|
{
appendComponentStack: true,
breakOnConsoleErrors: false,
showInlineWarningsAndErrors: true,
hideConsoleLogsInStrictMode: false
}
| + ### `connectToDevTools` options -| Prop | Default | Description | -|---|---|---| -| `host` | `"localhost"` | Socket connection to frontend should use this host. | -| `isAppActive` | | (Optional) function that returns true/false, telling DevTools when it's ready to connect to React. | -| `port` | `8097` | Socket connection to frontend should use this port. | -| `resolveRNStyle` | | (Optional) function that accepts a key (number) and returns a style (object); used by React Native. | -| `retryConnectionDelay` | `200` | Delay (ms) to wait between retrying a failed Websocket connection | -| `useHttps` | `false` | Socket connection to frontend should use secure protocol (wss). | -| `websocket` | | Custom `WebSocket` connection to frontend; overrides `host` and `port` settings. | +| Prop | Default | Description | +|------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------| +| `host` | `"localhost"` | Socket connection to frontend should use this host. | +| `isAppActive` | | (Optional) function that returns true/false, telling DevTools when it's ready to connect to React. | +| `port` | `8097` | Socket connection to frontend should use this port. | +| `resolveRNStyle` | | (Optional) function that accepts a key (number) and returns a style (object); used by React Native. | +| `retryConnectionDelay` | `200` | Delay (ms) to wait between retrying a failed Websocket connection | +| `useHttps` | `false` | Socket connection to frontend should use secure protocol (wss). | +| `websocket` | | Custom `WebSocket` connection to frontend; overrides `host` and `port` settings. | +| `onSettingsUpdated` | | A callback that will be called when the user updates the settings in the UI. You can use it for persisting user settings. | | ### `connectWithCustomMessagingProtocol` options -| Prop | Description | -|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `onSubscribe` | Function, which receives listener (function, with a single argument) as an argument. Called when backend subscribes to messages from the other end (frontend). | -| `onUnsubscribe` | Function, which receives listener (function) as an argument. Called when backend unsubscribes to messages from the other end (frontend). | -| `onMessage` | Function, which receives 2 arguments: event (string) and payload (any). Called when backend emits a message, which should be sent to the frontend. | +| Prop | Description | +|---------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `onSubscribe` | Function, which receives listener (function, with a single argument) as an argument. Called when backend subscribes to messages from the other end (frontend). | +| `onUnsubscribe` | Function, which receives listener (function) as an argument. Called when backend unsubscribes to messages from the other end (frontend). | +| `onMessage` | Function, which receives 2 arguments: event (string) and payload (any). Called when backend emits a message, which should be sent to the frontend. | +| `onSettingsUpdated` | A callback that will be called when the user updates the settings in the UI. You can use it for persisting user settings. | Unlike `connectToDevTools`, `connectWithCustomMessagingProtocol` returns a callback, which can be used for unsubscribing the backend from the global DevTools hook. diff --git a/packages/react-devtools-core/src/backend.js b/packages/react-devtools-core/src/backend.js index 84f537e875d00..1f2055832a3dd 100644 --- a/packages/react-devtools-core/src/backend.js +++ b/packages/react-devtools-core/src/backend.js @@ -20,7 +20,10 @@ import type { ComponentFilter, Wall, } from 'react-devtools-shared/src/frontend/types'; -import type {DevToolsHook} from 'react-devtools-shared/src/backend/types'; +import type { + DevToolsHook, + DevToolsHookSettings, +} from 'react-devtools-shared/src/backend/types'; import type {ResolveNativeStyle} from 'react-devtools-shared/src/backend/NativeStyleEditor/setupNativeStyleEditor'; type ConnectOptions = { @@ -32,12 +35,9 @@ type ConnectOptions = { retryConnectionDelay?: number, isAppActive?: () => boolean, websocket?: ?WebSocket, + onSettingsUpdated?: (settings: $ReadOnly) => void, }; -installHook(window); - -const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; - let savedComponentFilters: Array = getDefaultComponentFilters(); @@ -52,11 +52,21 @@ function debug(methodName: string, ...args: Array) { } } +export function initialize( + maybeSettingsOrSettingsPromise?: + | DevToolsHookSettings + | Promise, +) { + installHook(window, maybeSettingsOrSettingsPromise); +} + export function connectToDevTools(options: ?ConnectOptions) { + const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; if (hook == null) { // DevTools didn't get injected into this page (maybe b'c of the contentType). return; } + const { host = 'localhost', nativeStyleEditorValidAttributes, @@ -66,6 +76,7 @@ export function connectToDevTools(options: ?ConnectOptions) { resolveRNStyle = (null: $FlowFixMe), retryConnectionDelay = 2000, isAppActive = () => true, + onSettingsUpdated, } = options || {}; const protocol = useHttps ? 'wss' : 'ws'; @@ -160,7 +171,14 @@ export function connectToDevTools(options: ?ConnectOptions) { // TODO (npm-packages) Warn if "isBackendStorageAPISupported" // $FlowFixMe[incompatible-call] found when upgrading Flow const agent = new Agent(bridge); + if (onSettingsUpdated != null) { + agent.addListener('updateHookSettings', onSettingsUpdated); + } agent.addListener('shutdown', () => { + if (onSettingsUpdated != null) { + agent.removeListener('updateHookSettings', onSettingsUpdated); + } + // If we received 'shutdown' from `agent`, we assume the `bridge` is already shutting down, // and that caused the 'shutdown' event on the `agent`, so we don't need to call `bridge.shutdown()` here. hook.emit('shutdown'); @@ -290,6 +308,7 @@ type ConnectWithCustomMessagingOptions = { onMessage: (event: string, payload: any) => void, nativeStyleEditorValidAttributes?: $ReadOnlyArray, resolveRNStyle?: ResolveNativeStyle, + onSettingsUpdated?: (settings: $ReadOnly) => void, }; export function connectWithCustomMessagingProtocol({ @@ -298,7 +317,9 @@ export function connectWithCustomMessagingProtocol({ onMessage, nativeStyleEditorValidAttributes, resolveRNStyle, + onSettingsUpdated, }: ConnectWithCustomMessagingOptions): Function { + const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; if (hook == null) { // DevTools didn't get injected into this page (maybe b'c of the contentType). return; @@ -334,7 +355,14 @@ export function connectWithCustomMessagingProtocol({ } const agent = new Agent(bridge); + if (onSettingsUpdated != null) { + agent.addListener('updateHookSettings', onSettingsUpdated); + } agent.addListener('shutdown', () => { + if (onSettingsUpdated != null) { + agent.removeListener('updateHookSettings', onSettingsUpdated); + } + // If we received 'shutdown' from `agent`, we assume the `bridge` is already shutting down, // and that caused the 'shutdown' event on the `agent`, so we don't need to call `bridge.shutdown()` here. hook.emit('shutdown');