Skip to content

Commit

Permalink
Update DevTools READMEs
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn committed Mar 15, 2022
1 parent 0412f0c commit a008e45
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 34 deletions.
39 changes: 23 additions & 16 deletions packages/react-devtools-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,40 @@ The `config` object may contain:

## `react-devtools-core/standalone`

Renders the DevTools interface into a DOM node.

Renders DevTools UI into a DOM node:
```js
require("react-devtools-core/standalone")
.setContentDOMNode(document.getElementById("container"))
.setStatusListener(status => {
// This callback is optional...
})
.startServer(port);
```
import DevtoolsUI from "react-devtools-core/standalone";

Renders DevTools interface into a DOM node over SSL using a custom host name (Default is localhost).
const { setContentDOMNode, setStatusListener, startServer } = DevtoolsUI;

const container = document.getElementById("container");

setContentDOMNode(container);
setStatusListener(status => {
// This callback is optional...
});
startServer(port, host, options);
```

Configures DevTools UI to use SSL with custom host name:
```js
import DevtoolsUI from "react-devtools-core/standalone";

const { setContentDOMNode, setStatusListener, startServer } = DevtoolsUI;

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')
};

const container = document.getElementById("container");

require("react-devtools-core/standalone")
.setContentDOMNode(document.getElementById("container"))
.setStatusListener(status => {
// This callback is optional...
})
.startServer(port, host, options);
setContentDOMNode(container);
setStatusListener(status => {
// This callback is optional...
});
startServer(port, host, options);
```

Reference the `react-devtools` package for a complete integration example.
Expand Down
119 changes: 105 additions & 14 deletions packages/react-devtools-inline/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ React DevTools implementation for embedding within a browser-based IDE (e.g. [Co

This is a low-level package. If you're looking for the standalone DevTools app, **use the `react-devtools` package instead.**

## Usage
# 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,9 +16,9 @@ The frontend and backend can be initialized in any order, but **the backend must

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

## API
# API

### `react-devtools-inline/backend`
## `react-devtools-inline/backend`

* **`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>
Expand All @@ -43,7 +43,7 @@ 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.)

### `react-devtools-inline/frontend`
## `react-devtools-inline/frontend`

* **`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>.
Expand All @@ -62,9 +62,9 @@ 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
# Examples

### Supporting named hooks
## Supporting named hooks

DevTools can display hook "names" for an inspected component, although determining the "names" requires loading the source (and source-maps), parsing the code, and inferring the names based on which variables hook values get assigned to. Because the code for this is non-trivial, it's lazy-loaded only if the feature is enabled.

Expand All @@ -81,7 +81,7 @@ const hookNamesModuleLoaderFunction = () => import('react-devtools-inline/hookNa
/>;
```

### Configuring a same-origin `iframe`
## Configuring a same-origin `iframe`

The simplest way to use this package is to install the hook from the parent `window`. This is possible if the `iframe` is not sandboxed and there are no cross-origin restrictions.

Expand Down Expand Up @@ -116,7 +116,7 @@ const DevTools = initializeFrontend(contentWindow);
activateBackend(contentWindow);
```

### Configuring a sandboxed `iframe`
## Configuring a sandboxed `iframe`

Sandboxed `iframe`s are also supported but require more complex initialization.

Expand Down Expand Up @@ -169,7 +169,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 +235,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.
12 changes: 10 additions & 2 deletions packages/react-devtools/OVERVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,11 +293,13 @@ To mitigate the performance impact of re-rendering a component, DevTools does th

## Profiler

The Profiler UI is a powerful tool for identifying and fixing performance problems. The primary goal of the new profiler is to minimize its impact (CPU usage) while profiling is active. This can be accomplished by:
DevTools provides a suite of profiling tools for identifying and fixing performance problems. React 16.9+ supports a "legacy" profiler and React 18+ adds the ["timeline" profiler](https://github.com/facebook/react/tree/main/packages/react-devtools-timeline/src) support. These profilers are explained below, but at a high level– the architecture of each profiler aims to minimize the impact (CPU usage) while profiling is active. This can be accomplished by:
* Minimizing bridge traffic.
* Making expensive computations lazy.

The majority of profiling information is stored on the backend. The backend push-notifies the frontend of when profiling starts or stops by sending a "_profilingStatus_" message. The frontend also asks for the current status after mounting by sending a "_getProfilingStatus_" message. (This is done to support the reload-and-profile functionality.)
The majority of profiling information is stored in the DevTools backend. The backend push-notifies the frontend of when profiling starts or stops by sending a "_profilingStatus_" message. The frontend also asks for the current status after mounting by sending a "_getProfilingStatus_" message. (This is done to support the reload-and-profile functionality.)

### Legacy profiler

When profiling begins, the frontend takes a snapshot/copy of each root. This snapshot includes the id, name, key, and child IDs for each node in the tree. (This information is already present on the frontend, so it does not require any additional bridge traffic.) While profiling is active, each time React commits– the frontend also stores a copy of the "_operations_" message (described above). Once profiling has finished, the frontend can use the original snapshot along with each of the stored "_operations_" messages to reconstruct the tree for each of the profiled commits.

Expand All @@ -308,6 +310,12 @@ When profiling begins, the backend records the base durations of each fiber curr

This information will eventually be required by the frontend in order to render its profiling graphs, but it will not be sent across the bridge until profiling has completed (to minimize the performance impact of profiling).

### Timeline profiler

Timeline profiling data can come from one of two places:
* The React DevTools backend, which injects a [set of profiling hooks](https://github.com/facebook/react/blob/main/packages/react-devtools-shared/src/backend/profilingHooks.js) that React calls while rendering. When profiling, these hooks store information in memory which gets passed to DevTools when profiling is stopped.
* A Chrome performance export (JSON) containing React data (as User Timing marks) and other browser data like CPU samples, Network traffic, and native commits. (This method is not as convenient but provides more detailed browser performance data.)

### Combining profiling data

Once profiling is finished, the frontend requests profiling data from the backend one renderer at a time by sending a "_getProfilingData_" message. The backend responds with a "_profilingData_" message that contains per-root commit timing and duration information. The frontend then combines this information with its own snapshots to form a complete picture of the profiling session. Using this data, charts and graphs are lazily computed (and incrementally cached) on demand, based on which commits and views are selected in the Profiler UI.
Expand Down
4 changes: 2 additions & 2 deletions packages/react-devtools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ Run `react-devtools` from the terminal to launch the standalone DevTools app:
react-devtools
```

If you're not in a simulator then you also need to run the following in a command prompt:
If you're not using a local simulator, you'll also need to forward ports used by React DevTools:
```sh
adb reverse tcp:8097 tcp:8097
```

If you're using React Native 0.43 or higher, it should connect to your simulator within a few seconds.
If you're using React Native 0.43 or higher, it should connect to your simulator within a few seconds. (If this doesn't happen automatically, try reloading the React Native app.)

### Integration with React Native Inspector

Expand Down

0 comments on commit a008e45

Please sign in to comment.