Skip to content

Commit

Permalink
Update DevTools READMEs (#24105)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn authored Mar 16, 2022
1 parent ef23a9e commit 8fce116
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 69 deletions.
140 changes: 99 additions & 41 deletions packages/react-devtools-core/README.md
Original file line number Diff line number Diff line change
@@ -1,66 +1,122 @@
# `react-devtools-core`

A standalone React DevTools implementation.
This package provides low-level APIs to support renderers like [React Native](https://github.com/facebook/react-native). If you're looking for the standalone React DevTools UI, **we suggest using [`react-devtools`](https://github.com/facebook/react/tree/main/packages/react-devtools) instead of using this package directly**.

This is a low-level package. If you're looking for the Electron app you can run, **use `react-devtools` package instead.**
This package provides two entrypoints: labeled "backend" and "standalone" (frontend). Both APIs are described below.

## API
# Backend API

### `react-devtools-core`
Backend APIs are embedded in _development_ builds of renderers like [React Native](https://github.com/facebook/react-native) in order to connect to the React DevTools UI.

This is similar requiring the `react-devtools` package, but provides several configurable options. Unlike `react-devtools`, requiring `react-devtools-core` doesn't connect immediately but instead exports a function:
### Example

If you are building a non-browser-based React renderer, you can use the backend API like so:

```js
const { connectToDevTools } = require("react-devtools-core");
connectToDevTools(config);
if (process.env.NODE_ENV !== 'production') {
const { connectToDevTools } = require("react-devtools-core");

// Must be called before packages like react or react-native are imported
connectToDevTools({
...config
});
}
```

Run `connectToDevTools()` in the same context as React to set up a connection to DevTools.
Be sure to run this function *before* importing e.g. `react`, `react-dom`, `react-native`.
> **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`).
### `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. |

# Frontend API

Frontend APIs can be used to render the DevTools UI into a DOM node. One example of this is [`react-devtools`](https://github.com/facebook/react/tree/main/packages/react-devtools) which wraps DevTools in an Electron app.

### Example
```js
import DevtoolsUI from "react-devtools-core/standalone";

// See the full list of API methods in documentation below.
const { setContentDOMNode, startServer } = DevtoolsUI;

// Render DevTools UI into a DOM element.
setContentDOMNode(document.getElementById("container"));

// Start socket server used to communicate between backend and frontend.
startServer(
// Port defaults to 8097
1234,

The `config` object may contain:
* `host: string` (defaults to "localhost") - Websocket will connect to this host.
* `port: number` (defaults to `8097`) - Websocket will connect to this port.
* `useHttps: boolean` (defaults to `false`) - Websocket should use a secure protocol (wss).
* `websocket: Websocket` - Custom websocket to use. Overrides `host` and `port` settings if provided.
* `resolveRNStyle: (style: number) => ?Object` - Used by the React Native style plug-in.
* `retryConnectionDelay: number` (defaults to `2000`) - Milliseconds delay to wait between retrying a failed Websocket connection.
* `isAppActive: () => boolean` - If provided, DevTools will poll this method and wait until it returns true before connecting to React.
// Host defaults to "localhost"
"example.devserver.com",

## `react-devtools-core/standalone`
// Optional config for secure socket (WSS).
{
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
}
);
```

Renders the DevTools interface into a DOM node.
### Exported methods
The `default` export is an object defining the methods described below.

These methods support chaining for convenience. For example:
```js
require("react-devtools-core/standalone")
.setContentDOMNode(document.getElementById("container"))
.setStatusListener(status => {
// This callback is optional...
})
.startServer(port);
const DevtoolsUI = require("react-devtools-core/standalone");
DevtoolsUI.setContentDOMNode(element).startServer();
```

Renders DevTools interface into a DOM node over SSL using a custom host name (Default is localhost).
#### `connectToSocket(socket: WebSocket)`
> This is an advanced config function that is typically not used.
Custom `WebSocket` connection to use for communication between DevTools frontend and backend. Calling this method automatically initializes the DevTools UI (similar to calling `startServer()`).

#### `openProfiler()`
Automatically select the "Profiler" tab in the DevTools UI.

#### `setContentDOMNode(element: HTMLElement)`
Set the DOM element DevTools UI should be rendered into on initialization.

#### `setDisconnectedCallback(callback: Function)`
_Optional_ callback to be notified when DevTools `WebSocket` closes (or errors).

#### `setProjectRoots(roots: Array<string>)`
_Optional_ set of root directores for source files. These roots can be used to open an inspected component's source code using an IDE.

#### `setStatusListener(callback: Function)`
_Optional_ callback to be notified of socket server events (e.g. initialized, errored, connected).

This callback receives two parameters:
```js
const host = 'dev.server.com';
const options = {
key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};


require("react-devtools-core/standalone")
.setContentDOMNode(document.getElementById("container"))
.setStatusListener(status => {
// This callback is optional...
})
.startServer(port, host, options);
function onStatus(
message: string,
status: 'server-connected' | 'devtools-connected' | 'error'
): void {
// ...
}
```

Reference the `react-devtools` package for a complete integration example.
#### `startServer(port?: number, host?: string, httpsOptions?: Object, loggerOptions?: Object)`
Start a socket server (used to communicate between backend and frontend) and renders the DevTools UI.

## Development
This method accepts the following parameters:
| Name | Default | Description |
|---|---|---|
| `port` | `8097` | Socket connection to backend should use this port. |
| `host` | `"localhost"` | Socket connection to backend should use this host. |
| `httpsOptions` | | _Optional_ object defining `key` and `cert` strings. |
| `loggerOptions` | | _Optional_ object defining a `surface` string (to be included with DevTools logging events). |

# Development

Watch for changes made to the backend entry point and rebuild:
```sh
Expand All @@ -71,3 +127,5 @@ Watch for changes made to the standalone UI entry point and rebuild:
```sh
yarn start:standalone
```

Run the standalone UI using `yarn start` in the [`react-devtools`](https://github.com/facebook/react/tree/main/packages/react-devtools).
141 changes: 120 additions & 21 deletions packages/react-devtools-inline/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
# `react-devtools-inline`

React DevTools implementation for embedding within a browser-based IDE (e.g. [CodeSandbox](https://codesandbox.io/), [StackBlitz](https://stackblitz.com/)).
This package can be used to embed React DevTools into browser-based tools like [CodeSandbox](https://codesandbox.io/), [StackBlitz](https://stackblitz.com/), and [Replay](https://replay.io).

This is a low-level package. If you're looking for the standalone DevTools app, **use the `react-devtools` package instead.**
If you're looking for the standalone React DevTools UI, **we suggest using [`react-devtools`](https://github.com/facebook/react/tree/main/packages/react-devtools) instead of using this package directly**.

## Usage
---

> **Note** that this package (and the DevTools UI) relies on several _experimental_ APIs that are **only available in the [experimental release channel](https://reactjs.org/docs/release-channels.html#experimental-channel)**. This means that you will need to install `react@experimental` and `react-dom@experimenal`.
---

# Usage

This package exports two entry points: a frontend (to be run in the main `window`) and a backend (to be installed and run within an `iframe`<sup>1</sup>).

Expand All @@ -16,15 +22,18 @@ The frontend and backend can be initialized in any order, but **the backend must

<sup>1</sup> Sandboxed iframes are supported.

## API
# Backend APIs
### `initialize(windowOrGlobal)`

Installs the global hook on the window/global object. This hook is how React and DevTools communicate.

### `react-devtools-inline/backend`
> **This method must be called before React is loaded.** (This includes `import`/`require` statements and `<script>` tags that include React.)
### `activate(windowOrGlobal)`

* **`initialize(contentWindow)`** -
Installs the global hook on the window. This hook is how React and DevTools communicate. **This method must be called before React is loaded.**<sup>2</sup>
* **`activate(contentWindow)`** -
Lets the backend know when the frontend is ready. It should not be called until after the frontend has been initialized, else the frontend might miss important tree-initialization events.

### Example
```js
import { activate, initialize } from 'react-devtools-inline/backend';

Expand All @@ -41,13 +50,14 @@ initialize(contentWindow);
activate(contentWindow);
```

<sup>2</sup> The backend must be initialized before React is loaded. (This means before any `import` or `require` statements or `<script>` tags that include React.)
# Frontend APIs

### `react-devtools-inline/frontend`
### `initialize(windowOrGlobal)`
Configures the DevTools interface to listen to the `window` (or `global` object) the backend was injected into. This method returns a React component that can be rendered directly.

* **`initialize(contentWindow)`** -
Configures the DevTools interface to listen to the `window` the backend was injected into. This method returns a React component that can be rendered directly<sup>3</sup>.
> Because the DevTools interface makes use of several new React concurrent features (like Suspense) **it should be rendered using `ReactDOMClient.createRoot` instead of `ReactDOM.render`.**
### Example
```js
import { initialize } from 'react-devtools-inline/frontend';

Expand All @@ -60,9 +70,7 @@ const contentWindow = iframe.contentWindow;
const DevTools = initialize(contentWindow);
```

<sup>3</sup> Because the DevTools interface makes use of several new React APIs (e.g. suspense, concurrent mode) it should be rendered using `ReactDOMClient.createRoot`. **It should not be rendered with `ReactDOM.render`.**

## Examples
# Advanced examples

### Supporting named hooks

Expand Down Expand Up @@ -169,7 +177,7 @@ iframe.onload = () => {
};
```

### Advanced integration with custom "wall"
### Advanced: Custom "wall"

Below is an example of an advanced integration with a website like [Replay.io](https://replay.io/) or Code Sandbox's Sandpack (where more than one DevTools instance may be rendered per page).

Expand Down Expand Up @@ -235,25 +243,116 @@ const wall = {
};
```

## Local development
### Advanced: Node + browser

Below is an example of an advanced integration that could be used to connect React running in a Node process to React DevTools running in a browser.

##### Sample Node backend
```js
const {
activate,
createBridge,
initialize,
} = require('react-devtools-inline/backend');
const { createServer } = require('http');
const SocketIO = require('socket.io');

const server = createServer();
const socket = SocketIO(server, {
cors: {
origin: "*",
methods: ["GET", "POST"],
allowedHeaders: [],
credentials: true
}
});
socket.on('connection', client => {
const wall = {
listen(listener) {
client.on('message', data => {
if (data.uid === UID) {
listener(data);
}
});
},
send(event, payload) {
const data = {event, payload, uid: UID};
client.emit('message', data);
},
};

const bridge = createBridge(global, wall);

client.on('disconnect', () => {
bridge.shutdown();
});

activate(global, { bridge });
});
socket.listen(PORT);
```

##### Sample Web frontend
```js
import { createElement } from 'react';
import { createRoot } from 'react-dom/client';
import {
createBridge,
createStore,
initialize as createDevTools,
} from 'react-devtools-inline/frontend';
import { io } from "socket.io-client";

let root = null;

const socket = io(`http://${HOST}:${PORT}`);
socket.on("connect", () => {
const wall = {
listen(listener) {
socket.on("message", (data) => {
if (data.uid === UID) {
listener(data);
}
});
},
send(event, payload) {
const data = { event, payload, uid: UID };
socket.emit('message', data);
},
};

const bridge = createBridge(window, wall);
const store = createStore(bridge);
const DevTools = createDevTools(window, { bridge, store });

root = createRoot(document.getElementById('root'));
root.render(createElement(DevTools));
});
socket.on("disconnect", () => {
root.unmount();
root = null;
});
```

# Local development
You can also build and test this package from source.

### Prerequisite steps
## Prerequisite steps
DevTools depends on local versions of several NPM packages<sup>1</sup> also in this workspace. You'll need to either build or download those packages first.

<sup>1</sup> Note that at this time, an _experimental_ build is required because DevTools depends on the `createRoot` API.

#### Build from source
### Build from source
To build dependencies from source, run the following command from the root of the repository:
```sh
yarn build-for-devtools
```
#### Download from CI
### Download from CI
To use the latest build from CI, run the following command from the root of the repository:
```sh
./scripts/release/download-experimental-build.js
```
### Build steps
## Build steps
Once the above packages have been built or downloaded, you can watch for changes made to the source code and automatically rebuild by running:
```sh
yarn start
Expand Down
6 changes: 6 additions & 0 deletions packages/react-devtools-shared/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
This directory contains code shared between several DevTools packages:
* /packages/react-devtools-core
* /packages/react-devtools-extensions
* /packages/react-devtools-inline

It is not published or released anywhere directly.
Loading

0 comments on commit 8fce116

Please sign in to comment.