Show status of connection, so that user knows what is happening

Summary: In many cases the React DevTools fail to show up for RN. usually that is because the app didn't enter devMode yet. This diff adds the necessary logic to query the state of the DevTools and communicate back the current connection status

Reviewed By: passy

Differential Revision: D19878127

fbshipit-source-id: f5c3f5a92b23c87c87d778a468122210325eed17
This commit is contained in:
Michel Weststrate
2020-02-17 03:36:59 -08:00
committed by Facebook Github Bot
parent 14ebfb8439
commit 3849807d6b
2 changed files with 82 additions and 10 deletions

View File

@@ -7,9 +7,8 @@
* @format
*/
import ReactDOM from 'react-dom';
import ReactDevToolsStandalone from 'react-devtools-core/standalone';
import {FlipperPlugin, AndroidDevice, styled} from 'flipper';
import {FlipperPlugin, AndroidDevice, styled, View, Toolbar} from 'flipper';
import React from 'react';
import getPort from 'get-port';
import address from 'address';
@@ -19,6 +18,7 @@ const Container = styled.div({
flex: '1 1 0%',
justifyContent: 'center',
alignItems: 'stretch',
height: '100%',
});
const DEV_TOOLS_NODE_ID = 'reactdevtools-out-of-react-node';
@@ -43,7 +43,7 @@ function findDevToolsNode(): HTMLElement | null {
}
function attachDevTools(target: Element | Text, devToolsNode: HTMLElement) {
target.insertBefore(devToolsNode, target.childNodes[0]);
target.appendChild(devToolsNode);
devToolsNode.style.display = 'flex';
}
@@ -52,27 +52,89 @@ function detachDevTools(devToolsNode: HTMLElement) {
document.body && document.body.appendChild(devToolsNode);
}
export default class extends FlipperPlugin<{}, any, {}> {
const CONNECTED = 'DevTools connected';
export default class extends FlipperPlugin<
{
status: string;
},
any,
{}
> {
pollHandle?: NodeJS.Timeout;
containerRef: React.RefObject<HTMLDivElement> = React.createRef();
triedToAutoConnect = false;
state = {
status: 'initializing',
};
componentDidMount() {
let devToolsNode = findDevToolsNode();
if (!devToolsNode) {
devToolsNode = createDevToolsNode();
this.initializeDevTools(devToolsNode);
} else {
this.setStatus(
'DevTools have been initialized, waiting for connection...',
);
if (devToolsNode.innerHTML) {
this.setStatus(CONNECTED);
} else {
this.startPollForConnection();
}
}
const currentComponentNode = ReactDOM.findDOMNode(this);
currentComponentNode && attachDevTools(currentComponentNode, devToolsNode);
attachDevTools(this.containerRef?.current!, devToolsNode);
this.startPollForConnection();
}
componentWillUnmount() {
if (this.pollHandle) {
clearTimeout(this.pollHandle);
}
const devToolsNode = findDevToolsNode();
devToolsNode && detachDevTools(devToolsNode);
}
setStatus(status: string) {
console.log(`[ReactDevtoolsPlugin] ${status}`);
if (status.startsWith('The server is listening on')) {
this.setState({status: status + ' Waiting for connection...'});
} else {
this.setState({status});
}
}
startPollForConnection() {
this.pollHandle = setTimeout(() => {
if (findDevToolsNode()?.innerHTML) {
this.setStatus(CONNECTED);
} else {
if (!this.triedToAutoConnect) {
this.triedToAutoConnect = true;
this.setStatus(
"The DevTools didn't connect yet. Please open the DevMenu or Reload to connect",
);
// TODO: send reload command
}
this.startPollForConnection();
}
}, 3000);
}
async initializeDevTools(devToolsNode: HTMLElement) {
this.setStatus('Waiting for port 8097');
const port = await getPort({port: 8097}); // default port for dev tools
ReactDevToolsStandalone.setContentDOMNode(devToolsNode).startServer(port);
this.setStatus('Starting DevTools server on ' + port);
ReactDevToolsStandalone.setContentDOMNode(devToolsNode)
.setStatusListener(status => {
this.setStatus(status);
})
.startServer(port);
this.setStatus('Waiting for device');
const device = await this.getDevice();
if (device) {
const host =
device.deviceType === 'physical'
@@ -80,16 +142,25 @@ export default class extends FlipperPlugin<{}, any, {}> {
: device instanceof AndroidDevice
? '10.0.2.2' // Host IP for Android emulator host system
: 'localhost';
this.setStatus(`Updating config to ${host}:${port}`);
this.client.call('config', {port, host});
if (['quest', 'go', 'pacific'].includes(device.title.toLowerCase())) {
const device = await this.getDevice();
this.setStatus(`Setting up reverse port mapping: ${port}:${port}`);
(device as AndroidDevice).reverse([port, port]);
}
}
}
render() {
return <Container />;
return (
<View grow>
{this.state.status !== CONNECTED ? (
<Toolbar>{this.state.status}</Toolbar>
) : null}
<Container ref={this.containerRef} />
</View>
);
}
}

View File

@@ -9,8 +9,9 @@
declare module 'react-devtools-core/standalone' {
interface DevTools {
setContentDOMNode(node: HTMLElement): DevTools;
startServer(port: number): DevTools;
setContentDOMNode(node: HTMLElement): this;
startServer(port: number): this;
setStatusListener(listener: (message: string) => void): this;
}
const DevTools: DevTools;
export default DevTools;