@samitouri / QOS-React-2 / commits / 8fce116998

Update DevTools READMEs (#24105)

Brian Vaughn committed Mar 16, 2022 at 08:37 UTC 8fce116998ea525194e02075d1b89ddafc8d698f
5 files changed +238 -69
packages/react-devtools-core/README.md
+99 -41
@@ -1,66 +1,122 @@
1 # `react-devtools-core`
2
3 -A standalone React DevTools implementation.
3 +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**.
4
5 -This is a low-level package. If you're looking for the Electron app you can run, **use `react-devtools` package instead.**
5 +This package provides two entrypoints: labeled "backend" and "standalone" (frontend). Both APIs are described below.
6
7 -## API
7 +# Backend API
8
9 -### `react-devtools-core`
9 +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.
10
11 -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:
11 +### Example
12 +
13 +If you are building a non-browser-based React renderer, you can use the backend API like so:
14
15 ```js
14 -const { connectToDevTools } = require("react-devtools-core");
15 -connectToDevTools(config);
16 +if (process.env.NODE_ENV !== 'production') {
17 + const { connectToDevTools } = require("react-devtools-core");
18 +
19 + // Must be called before packages like react or react-native are imported
20 + connectToDevTools({
21 + ...config
22 + });
23 +}
24 ```
25
18 -Run `connectToDevTools()` in the same context as React to set up a connection to DevTools.
19 -Be sure to run this function *before* importing e.g. `react`, `react-dom`, `react-native`.
26 +> **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`).
27 +
28 +### `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 +
39 +# Frontend API
40 +
41 +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.
42 +
43 +### Example
44 +```js
45 +import DevtoolsUI from "react-devtools-core/standalone";
46 +
47 +// See the full list of API methods in documentation below.
48 +const { setContentDOMNode, startServer } = DevtoolsUI;
49 +
50 +// Render DevTools UI into a DOM element.
51 +setContentDOMNode(document.getElementById("container"));
52 +
53 +// Start socket server used to communicate between backend and frontend.
54 +startServer(
55 + // Port defaults to 8097
56 + 1234,
57
21 -The `config` object may contain:
22 -* `host: string` (defaults to "localhost") - Websocket will connect to this host.
23 -* `port: number` (defaults to `8097`) - Websocket will connect to this port.
24 -* `useHttps: boolean` (defaults to `false`) - Websocket should use a secure protocol (wss).
25 -* `websocket: Websocket` - Custom websocket to use. Overrides `host` and `port` settings if provided.
26 -* `resolveRNStyle: (style: number) => ?Object` - Used by the React Native style plug-in.
27 -* `retryConnectionDelay: number` (defaults to `2000`) - Milliseconds delay to wait between retrying a failed Websocket connection.
28 -* `isAppActive: () => boolean` - If provided, DevTools will poll this method and wait until it returns true before connecting to React.
58 + // Host defaults to "localhost"
59 + "example.devserver.com",
60
30 -## `react-devtools-core/standalone`
61 + // Optional config for secure socket (WSS).
62 + {
63 + key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
64 + cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
65 + }
66 +);
67 +```
68
32 -Renders the DevTools interface into a DOM node.
69 +### Exported methods
70 +The `default` export is an object defining the methods described below.
71
72 +These methods support chaining for convenience. For example:
73 ```js
35 -require("react-devtools-core/standalone")
36 - .setContentDOMNode(document.getElementById("container"))
37 - .setStatusListener(status => {
38 - // This callback is optional...
39 - })
40 - .startServer(port);
74 +const DevtoolsUI = require("react-devtools-core/standalone");
75 +DevtoolsUI.setContentDOMNode(element).startServer();
76 ```
77
43 -Renders DevTools interface into a DOM node over SSL using a custom host name (Default is localhost).
78 +#### `connectToSocket(socket: WebSocket)`
79 +> This is an advanced config function that is typically not used.
80 +
81 +Custom `WebSocket` connection to use for communication between DevTools frontend and backend. Calling this method automatically initializes the DevTools UI (similar to calling `startServer()`).
82 +
83 +#### `openProfiler()`
84 +Automatically select the "Profiler" tab in the DevTools UI.
85 +
86 +#### `setContentDOMNode(element: HTMLElement)`
87 +Set the DOM element DevTools UI should be rendered into on initialization.
88 +
89 +#### `setDisconnectedCallback(callback: Function)`
90 +_Optional_ callback to be notified when DevTools `WebSocket` closes (or errors).
91
92 +#### `setProjectRoots(roots: Array<string>)`
93 +_Optional_ set of root directores for source files. These roots can be used to open an inspected component's source code using an IDE.
94 +
95 +#### `setStatusListener(callback: Function)`
96 +_Optional_ callback to be notified of socket server events (e.g. initialized, errored, connected).
97 +
98 +This callback receives two parameters:
99 ```js
46 -const host = 'dev.server.com';
47 -const options = {
48 - key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
49 - cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
50 -};
51 -
52 -
53 -require("react-devtools-core/standalone")
54 - .setContentDOMNode(document.getElementById("container"))
55 - .setStatusListener(status => {
56 - // This callback is optional...
57 - })
58 - .startServer(port, host, options);
100 +function onStatus(
101 + message: string,
102 + status: 'server-connected' | 'devtools-connected' | 'error'
103 +): void {
104 + // ...
105 +}
106 ```
107
61 -Reference the `react-devtools` package for a complete integration example.
108 +#### `startServer(port?: number, host?: string, httpsOptions?: Object, loggerOptions?: Object)`
109 +Start a socket server (used to communicate between backend and frontend) and renders the DevTools UI.
110
63 -## Development
111 +This method accepts the following parameters:
112 +| Name | Default | Description |
113 +|---|---|---|
114 +| `port` | `8097` | Socket connection to backend should use this port. |
115 +| `host` | `"localhost"` | Socket connection to backend should use this host. |
116 +| `httpsOptions` | | _Optional_ object defining `key` and `cert` strings. |
117 +| `loggerOptions` | | _Optional_ object defining a `surface` string (to be included with DevTools logging events). |
118 +
119 +# Development
120
121 Watch for changes made to the backend entry point and rebuild:
122 ```sh
@@ -71,3 +127,5 @@ Watch for changes made to the standalone UI entry point and rebuild:
127 ```sh
128 yarn start:standalone
129 ```
130 +
131 +Run the standalone UI using `yarn start` in the [`react-devtools`](https://github.com/facebook/react/tree/main/packages/react-devtools).
\ No newline at end of file
packages/react-devtools-inline/README.md
+120 -21
@@ -1,10 +1,16 @@
1 # `react-devtools-inline`
2
3 -React DevTools implementation for embedding within a browser-based IDE (e.g. [CodeSandbox](https://codesandbox.io/), [StackBlitz](https://stackblitz.com/)).
3 +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).
4
5 -This is a low-level package. If you're looking for the standalone DevTools app, **use the `react-devtools` package instead.**
5 +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**.
6
7 -## Usage
7 +---
8 +
9 +> **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`.
10 +
11 +---
12 +
13 +# Usage
14
15 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>).
16
@@ -16,15 +22,18 @@ The frontend and backend can be initialized in any order, but **the backend must
22
23 <sup>1</sup> Sandboxed iframes are supported.
24
19 -## API
25 +# Backend APIs
26 +### `initialize(windowOrGlobal)`
27 +
28 +Installs the global hook on the window/global object. This hook is how React and DevTools communicate.
29
21 -### `react-devtools-inline/backend`
30 +> **This method must be called before React is loaded.** (This includes `import`/`require` statements and `<script>` tags that include React.)
31 +
32 +### `activate(windowOrGlobal)`
33
23 -* **`initialize(contentWindow)`** -
24 -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>
25 -* **`activate(contentWindow)`** -
34 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.
35
36 +### Example
37 ```js
38 import { activate, initialize } from 'react-devtools-inline/backend';
39
@@ -41,13 +50,14 @@ initialize(contentWindow);
50 activate(contentWindow);
51 ```
52
44 -<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.)
53 +# Frontend APIs
54
46 -### `react-devtools-inline/frontend`
55 +### `initialize(windowOrGlobal)`
56 +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.
57
48 -* **`initialize(contentWindow)`** -
49 -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>.
58 +> 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`.**
59
60 +### Example
61 ```js
62 import { initialize } from 'react-devtools-inline/frontend';
63
@@ -60,9 +70,7 @@ const contentWindow = iframe.contentWindow;
70 const DevTools = initialize(contentWindow);
71 ```
72
63 -<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`.**
64 -
65 -## Examples
73 +# Advanced examples
74
75 ### Supporting named hooks
76
@@ -169,7 +177,7 @@ iframe.onload = () => {
177 };
178 ```
179
172 -### Advanced integration with custom "wall"
180 +### Advanced: Custom "wall"
181
182 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).
183
@@ -235,25 +243,116 @@ const wall = {
243 };
244 ```
245
238 -## Local development
246 +### Advanced: Node + browser
247 +
248 +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.
249 +
250 +##### Sample Node backend
251 +```js
252 +const {
253 + activate,
254 + createBridge,
255 + initialize,
256 +} = require('react-devtools-inline/backend');
257 +const { createServer } = require('http');
258 +const SocketIO = require('socket.io');
259 +
260 +const server = createServer();
261 +const socket = SocketIO(server, {
262 + cors: {
263 + origin: "*",
264 + methods: ["GET", "POST"],
265 + allowedHeaders: [],
266 + credentials: true
267 + }
268 +});
269 +socket.on('connection', client => {
270 + const wall = {
271 + listen(listener) {
272 + client.on('message', data => {
273 + if (data.uid === UID) {
274 + listener(data);
275 + }
276 + });
277 + },
278 + send(event, payload) {
279 + const data = {event, payload, uid: UID};
280 + client.emit('message', data);
281 + },
282 + };
283 +
284 + const bridge = createBridge(global, wall);
285 +
286 + client.on('disconnect', () => {
287 + bridge.shutdown();
288 + });
289 +
290 + activate(global, { bridge });
291 +});
292 +socket.listen(PORT);
293 +```
294 +
295 +##### Sample Web frontend
296 +```js
297 +import { createElement } from 'react';
298 +import { createRoot } from 'react-dom/client';
299 +import {
300 + createBridge,
301 + createStore,
302 + initialize as createDevTools,
303 +} from 'react-devtools-inline/frontend';
304 +import { io } from "socket.io-client";
305 +
306 +let root = null;
307 +
308 +const socket = io(`http://${HOST}:${PORT}`);
309 +socket.on("connect", () => {
310 + const wall = {
311 + listen(listener) {
312 + socket.on("message", (data) => {
313 + if (data.uid === UID) {
314 + listener(data);
315 + }
316 + });
317 + },
318 + send(event, payload) {
319 + const data = { event, payload, uid: UID };
320 + socket.emit('message', data);
321 + },
322 + };
323 +
324 + const bridge = createBridge(window, wall);
325 + const store = createStore(bridge);
326 + const DevTools = createDevTools(window, { bridge, store });
327 +
328 + root = createRoot(document.getElementById('root'));
329 + root.render(createElement(DevTools));
330 +});
331 +socket.on("disconnect", () => {
332 + root.unmount();
333 + root = null;
334 +});
335 +```
336 +
337 +# Local development
338 You can also build and test this package from source.
339
241 -### Prerequisite steps
340 +## Prerequisite steps
341 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.
342
343 <sup>1</sup> Note that at this time, an _experimental_ build is required because DevTools depends on the `createRoot` API.
344
246 -#### Build from source
345 +### Build from source
346 To build dependencies from source, run the following command from the root of the repository:
347 ```sh
348 yarn build-for-devtools
349 ```
251 -#### Download from CI
350 +### Download from CI
351 To use the latest build from CI, run the following command from the root of the repository:
352 ```sh
353 ./scripts/release/download-experimental-build.js
354 ```
256 -### Build steps
355 +## Build steps
356 Once the above packages have been built or downloaded, you can watch for changes made to the source code and automatically rebuild by running:
357 ```sh
358 yarn start
packages/react-devtools-shared/README.md new
+6
@@ -0,0 +1,6 @@
1 +This directory contains code shared between several DevTools packages:
2 +* /packages/react-devtools-core
3 +* /packages/react-devtools-extensions
4 +* /packages/react-devtools-inline
5 +
6 +It is not published or released anywhere directly.
\ No newline at end of file
packages/react-devtools/OVERVIEW.md
+10 -2
@@ -293,11 +293,13 @@ To mitigate the performance impact of re-rendering a component, DevTools does th
293
294 ## Profiler
295
296 -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:
296 +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:
297 * Minimizing bridge traffic.
298 * Making expensive computations lazy.
299
300 -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.)
300 +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.)
301 +
302 +### Legacy profiler
303
304 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.
305
@@ -308,6 +310,12 @@ When profiling begins, the backend records the base durations of each fiber curr
310
311 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).
312
313 +### Timeline profiler
314 +
315 +Timeline profiling data can come from one of two places:
316 +* 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.
317 +* 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.)
318 +
319 ### Combining profiling data
320
321 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.
packages/react-devtools/README.md
+3 -5
@@ -1,8 +1,6 @@
1 # `react-devtools`
2
3 -React DevTools is available as a built-in extension for Chrome and Firefox browsers. This package enables you to debug a React app elsewhere (e.g. a mobile browser, an embedded webview, Safari, inside an iframe).
4 -
5 -It works both with React DOM and React Native.
3 +This package can be used to debug non-browser-based React applications (e.g. React Native, mobile browser or embedded webview, Safari).
4
5 ![React DevTools screenshot](https://user-images.githubusercontent.com/29597/63811956-bdd9b580-c8dd-11e9-8962-c568e475c425.png)
6
@@ -32,12 +30,12 @@ Run `react-devtools` from the terminal to launch the standalone DevTools app:
30 react-devtools
31 ```
32
35 -If you're not in a simulator then you also need to run the following in a command prompt:
33 +If you're not using a local simulator, you'll also need to forward ports used by React DevTools:
34 ```sh
35 adb reverse tcp:8097 tcp:8097
36 ```
37
40 -If you're using React Native 0.43 or higher, it should connect to your simulator within a few seconds.
38 +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.)
39
40 ### Integration with React Native Inspector
41