[react native] prepare the react native inspector for the new react devtools

Summary:
These are the changes needed for full interop with the (as yet unreleased) new
version of React Devtools.

- the on-device inspector is minimized when devtools is open
- devtools highlight -> device and device touch -> devtools select works
- editing react native styles :)
This commit is contained in:
Jared Forsyth 2015-07-14 12:03:17 -07:00
parent ebd046ae21
commit a43987309d
5 changed files with 88 additions and 3 deletions

View File

@ -20,10 +20,21 @@ var StyleSheet = require('StyleSheet');
var UIManager = require('NativeModules').UIManager;
var View = require('View');
var REACT_DEVTOOLS_HOOK: ?Object = typeof window !== 'undefined' ? window.__REACT_DEVTOOLS_BACKEND__ : null;
if (REACT_DEVTOOLS_HOOK) {
// required for devtools to be able to edit react native styles
REACT_DEVTOOLS_HOOK.resolveRNStyle = require('flattenStyle');
}
class Inspector extends React.Component {
_subs: ?Array<() => void>;
constructor(props: Object) {
super(props);
this.state = {
devtoolsBackend: null,
panelPos: 'bottom',
inspecting: true,
perfing: false,
@ -31,6 +42,63 @@ class Inspector extends React.Component {
};
}
componentDidMount() {
if (REACT_DEVTOOLS_HOOK) {
this.attachToDevtools = this.attachToDevtools.bind(this);
REACT_DEVTOOLS_HOOK.addStartupListener(this.attachToDevtools);
// if devtools is already started
// TODO(jared): should addStartupListener just go ahead and call the
// listener if the devtools is already started? might be unexpected...
// is there some name other than `addStartupListener` that would be
// better?
if (REACT_DEVTOOLS_HOOK.backend) {
this.attachToDevtools(REACT_DEVTOOLS_HOOK.backend);
}
}
}
componentWillUnmount() {
if (this._subs) {
this._subs.map(fn => fn());
}
if (REACT_DEVTOOLS_HOOK) {
REACT_DEVTOOLS_HOOK.removeStartupListener(this.attachToDevtools);
}
}
attachToDevtools(backend: Object) {
var _hideWait = null;
var hlSub = backend.sub('highlight', ({node, name, props}) => {
clearTimeout(_hideWait);
UIManager.measure(node, (x, y, width, height, left, top) => {
this.setState({
hierarchy: [],
inspected: {
frame: {left, top, width, height},
style: props ? props.style : {},
},
});
});
});
var hideSub = backend.sub('hideHighlight', () => {
// we wait to actually hide in order to avoid flicker
_hideWait = setTimeout(() => {
this.setState({
inspected: null,
});
}, 100);
});
this._subs = [hlSub, hideSub];
backend.on('shutdown', () => {
this.setState({devtoolsBackend: null});
this._subs = null;
});
this.setState({
devtoolsBackend: backend,
});
}
setSelection(i: number) {
var instance = this.state.hierarchy[i];
var publicInstance = instance.getPublicInstance();
@ -46,6 +114,9 @@ class Inspector extends React.Component {
}
onTouchInstance(instance: Object, frame: Object, pointerY: number) {
if (this.state.devtoolsBackend) {
this.state.devtoolsBackend.selectFromReactInstance(instance, true);
}
var hierarchy = InspectorUtils.getOwnerHierarchy(instance);
var publicInstance = instance.getPublicInstance();
var props = publicInstance.props || {};
@ -88,6 +159,7 @@ class Inspector extends React.Component {
/>}
<View style={[styles.panelContainer, panelContainerStyle]}>
<InspectorPanel
devtoolsIsOpen={!!this.state.devtoolsBackend}
inspecting={this.state.inspecting}
perfing={this.state.perfing}
setPerfing={this.setPerfing.bind(this)}

View File

@ -27,7 +27,11 @@ type EventLike = {
var InspectorOverlay = React.createClass({
propTypes: {
inspectedViewTag: PropTypes.object,
inspected: PropTypes.shape({
frame: PropTypes.object,
style: PropTypes.any,
}),
inspectedViewTag: PropTypes.number,
onTouchInstance: PropTypes.func.isRequired,
},

View File

@ -58,7 +58,7 @@ class InspectorPanel extends React.Component {
}
return (
<View style={styles.container}>
{contents}
{!this.props.devtoolsIsOpen && contents}
<View style={styles.buttonRow}>
<Button
title={'Inspect'}
@ -76,6 +76,7 @@ class InspectorPanel extends React.Component {
}
InspectorPanel.propTypes = {
devtoolsIsOpen: PropTypes.bool,
inspecting: PropTypes.bool,
setInspecting: PropTypes.func,
inspected: PropTypes.object,

View File

@ -79,6 +79,12 @@ var ReactNativeMount = {
_instancesByContainerID: {},
// these two functions are needed by React Devtools
findNodeHandle: require('findNodeHandle'),
nativeTagToRootNodeID: function (nativeTag: number): string {
return ReactNativeTagHandles.tagToRootNodeID[nativeTag];
},
/**
* @param {ReactComponent} instance Instance to render.
* @param {containerTag} containerView Handle to native view tag

View File

@ -1351,7 +1351,9 @@ RCT_EXPORT_METHOD(measure:(NSNumber *)reactTag
[self addUIBlock:^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
UIView *view = viewRegistry[reactTag];
if (!view) {
RCTLogError(@"measure cannot find view with tag #%@", reactTag);
// this view was probably collapsed out
RCTLogWarn(@"measure cannot find view with tag #%@", reactTag);
callback(@[]);
return;
}
CGRect frame = view.frame;