Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: expose installHook with settings argument from react-devtools-core/backend #30987

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 29 additions & 18 deletions packages/react-devtools-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| <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> |

### `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.

Expand Down
38 changes: 33 additions & 5 deletions packages/react-devtools-core/src/backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -32,12 +35,9 @@ type ConnectOptions = {
retryConnectionDelay?: number,
isAppActive?: () => boolean,
websocket?: ?WebSocket,
onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
};

installHook(window);

const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;

let savedComponentFilters: Array<ComponentFilter> =
getDefaultComponentFilters();

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

export function initialize(
maybeSettingsOrSettingsPromise?:
| DevToolsHookSettings
| Promise<DevToolsHookSettings>,
) {
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,
Expand All @@ -66,6 +76,7 @@ export function connectToDevTools(options: ?ConnectOptions) {
resolveRNStyle = (null: $FlowFixMe),
retryConnectionDelay = 2000,
isAppActive = () => true,
onSettingsUpdated,
} = options || {};

const protocol = useHttps ? 'wss' : 'ws';
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -290,6 +308,7 @@ type ConnectWithCustomMessagingOptions = {
onMessage: (event: string, payload: any) => void,
nativeStyleEditorValidAttributes?: $ReadOnlyArray<string>,
resolveRNStyle?: ResolveNativeStyle,
onSettingsUpdated?: (settings: $ReadOnly<DevToolsHookSettings>) => void,
};

export function connectWithCustomMessagingProtocol({
Expand All @@ -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;
Expand Down Expand Up @@ -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');
Expand Down
Loading