Files
react/packages/react-devtools-core
Robert Balicki fecc288b7d [react devtools] Device storage support (#25452)
# Summary
* This PR adds support for persisting certain settings to device
storage, allowing e.g. RN apps to properly patch the console when
restarted.
* The device storage APIs have signature `getConsolePatchSettings()` and
`setConsolePatchSettings(string)`, in iOS, are thin wrappers around the
`Library/Settings` turbomodule, and wrap a new TM that uses the `SharedPreferences` class in Android.
* Pass device storage getters/setters from RN to DevTools'
`connectToDevtools`. The setters are then used to populate values on
`window`. Later, the console is patched using these values.
* If we receive a notification from DevTools that the console patching
fields have been updated, we write values back to local storage.
* See https://github.com/facebook/react-native/pull/34903

# How did you test this change?
Manual testing, `yarn run test-build-devtools`, `yarn run prettier`,
`yarn run flow dom`

## Manual testing setup:

### React DevTools Frontend
* Get the DevTools frontend in flipper:
* `nvm install -g react-devtools-core`, then replace that package with a
symlink to the local package
  * enable "use globally installed devtools" in flipper
* yarn run start in react-devtools, etc. as well

### React DevTools Backend
* `yarn run build:backend` in react-devtools-core, then copy-paste that
file to the expo app's node_modules directory

### React Native
* A local version of React Native can be patched in by modifying an expo
app's package.json, as in `"react-native":
"rbalicki2/react-native#branch-name"`

# Versioning safety
* There are three versioned modules to worry about: react native, the
devtools frontend and the devtools backend.
* The react devtools backend checks for whether a `cachedSettingsStore`
is passed from react native. If not (e.g. if React Native is outdated),
then no behavior changes.
* The devtools backend reads the patched console values from the cached
settings store. However, if nothing has been stored, for example because
the frontend is outdated or has never synced its settings, then behavior
doesn't change.
* The devtools frontend sends no new messages. However, if it did send a
new message (e.g. "store this value at this key"), and the backend was
outdated, that message would be silently ignored.
2022-10-25 13:25:33 +11:00
..
2022-09-29 12:46:53 -04:00

react-devtools-core

This package provides low-level APIs to support renderers like React Native. If you're looking for the standalone React DevTools UI, we suggest using react-devtools instead of using this package directly.

This package provides two entrypoints: labeled "backend" and "standalone" (frontend). Both APIs are described below.

Backend API

Backend APIs are embedded in development builds of renderers like React Native in order to connect to the React DevTools UI.

Example

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

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

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 which wraps DevTools in an Electron app.

Example

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,

  // Host defaults to "localhost"
  "example.devserver.com",

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

Exported methods

The default export is an object defining the methods described below.

These methods support chaining for convenience. For example:

const DevtoolsUI = require("react-devtools-core/standalone");
DevtoolsUI.setContentDOMNode(element).startServer();

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 directories 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:

function onStatus(
  message: string,
  status: 'server-connected' | 'devtools-connected' | 'error'
): void {
  // ...
}

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.

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:

yarn start:backend

Watch for changes made to the standalone UI entry point and rebuild:

yarn start:standalone

Run the standalone UI using yarn start in the react-devtools.