refactor[Inspector]: migrate to functional component (#41411)

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/41411

Changelog: [Internal]

I am planning some changes in this component. No functional changes in this diff, except for the `panelContainerStyle` change for iOS to fix top gap.

Reviewed By: robhogan

Differential Revision: D50644902

fbshipit-source-id: 3111da100261552c89d0cd4eae724500c446cdfd
This commit is contained in:
Ruslan Lesiutin
2023-11-13 08:20:26 -08:00
committed by Facebook GitHub Bot
parent 3dd6a83c0e
commit 066cb2bfd5
5 changed files with 150 additions and 183 deletions
@@ -10,7 +10,9 @@
'use strict';
import type {InspectorData} from '../Renderer/shims/ReactNativeTypes';
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type {InspectedElementSource} from './Inspector';
const TouchableHighlight = require('../Components/Touchable/TouchableHighlight');
const TouchableWithoutFeedback = require('../Components/Touchable/TouchableWithoutFeedback');
@@ -25,13 +27,9 @@ const StyleInspector = require('./StyleInspector');
const React = require('react');
type Props = $ReadOnly<{|
hierarchy: Array<{|name: string|}>,
hierarchy: ?InspectorData['hierarchy'],
style?: ?ViewStyleProp,
source?: ?{
fileName?: string,
lineNumber?: number,
...
},
source?: ?InspectedElementSource,
frame?: ?Object,
selection?: ?number,
setSelection?: number => mixed,
@@ -63,23 +61,29 @@ class ElementProperties extends React.Component<Props> {
<TouchableWithoutFeedback>
<View style={styles.info}>
<View style={styles.breadcrumb}>
{mapWithSeparator(
this.props.hierarchy,
(hierarchyItem, i): React.MixedElement => (
<TouchableHighlight
key={'item-' + i}
style={[styles.breadItem, i === selection && styles.selected]}
// $FlowFixMe[not-a-function] found when converting React.createClass to ES6
onPress={() => this.props.setSelection(i)}>
<Text style={styles.breadItemText}>{hierarchyItem.name}</Text>
</TouchableHighlight>
),
(i): React.MixedElement => (
<Text key={'sep-' + i} style={styles.breadSep}>
&#9656;
</Text>
),
)}
{this.props.hierarchy != null &&
mapWithSeparator(
this.props.hierarchy,
(hierarchyItem, i): React.MixedElement => (
<TouchableHighlight
key={'item-' + i}
style={[
styles.breadItem,
i === selection && styles.selected,
]}
// $FlowFixMe[not-a-function] found when converting React.createClass to ES6
onPress={() => this.props.setSelection(i)}>
<Text style={styles.breadItemText}>
{hierarchyItem.name}
</Text>
</TouchableHighlight>
),
(i): React.MixedElement => (
<Text key={'sep-' + i} style={styles.breadSep}>
&#9656;
</Text>
),
)}
</View>
<View style={styles.row}>
<View style={styles.col}>
+116 -134
View File
@@ -10,7 +10,11 @@
'use strict';
import type {TouchedViewDataAtPoint} from '../Renderer/shims/ReactNativeTypes';
import type {
InspectorData,
TouchedViewDataAtPoint,
} from '../Renderer/shims/ReactNativeTypes';
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type {ReactDevToolsAgent} from '../Types/ReactDevToolsTypes';
import type {HostRef} from './getInspectorDataForViewAtPoint';
@@ -25,71 +29,68 @@ const InspectorOverlay = require('./InspectorOverlay');
const InspectorPanel = require('./InspectorPanel');
const React = require('react');
const {useState} = React;
type PanelPosition = 'top' | 'bottom';
type SelectedTab =
| 'elements-inspector'
| 'network-profiling'
| 'performance-profiling';
export type InspectedElementFrame = TouchedViewDataAtPoint['frame'];
export type InspectedElementSource = InspectorData['source'];
export type InspectedElement = $ReadOnly<{
frame: InspectedElementFrame,
source?: InspectedElementSource,
style?: ViewStyleProp,
}>;
export type ElementsHierarchy = InspectorData['hierarchy'];
type Props = {
inspectedView: ?HostRef,
onRequestRerenderApp: () => void,
reactDevToolsAgent?: ReactDevToolsAgent,
};
class Inspector extends React.Component<
Props,
{
hierarchy: any,
panelPos: string,
inspecting: boolean,
selection: ?number,
perfing: boolean,
inspected: any,
inspectedView: ?HostRef,
networking: boolean,
...
},
> {
_setTouchedViewData: ?(TouchedViewDataAtPoint) => void;
function Inspector({
inspectedView,
onRequestRerenderApp,
reactDevToolsAgent,
}: Props): React.Node {
const [selectedTab, setSelectedTab] =
useState<?SelectedTab>('elements-inspector');
constructor(props: Props) {
super(props);
const [panelPosition, setPanelPosition] = useState<PanelPosition>('bottom');
const [inspectedElement, setInspectedElement] =
useState<?InspectedElement>(null);
const [selectionIndex, setSelectionIndex] = useState<?number>(null);
const [elementsHierarchy, setElementsHierarchy] =
useState<?ElementsHierarchy>(null);
this.state = {
hierarchy: null,
panelPos: 'bottom',
inspecting: true,
perfing: false,
inspected: null,
selection: null,
inspectedView: this.props.inspectedView,
networking: false,
};
}
const setSelection = (i: number) => {
const hierarchyItem = elementsHierarchy?.[i];
if (hierarchyItem == null) {
return;
}
componentWillUnmount() {
this._setTouchedViewData = null;
}
UNSAFE_componentWillReceiveProps(newProps: Props) {
this.setState({inspectedView: newProps.inspectedView});
}
setSelection(i: number) {
const hierarchyItem = this.state.hierarchy[i];
// we pass in findNodeHandle as the method is injected
// We pass in findNodeHandle as the method is injected
const {measure, props, source} =
hierarchyItem.getInspectorData(findNodeHandle);
measure((x, y, width, height, left, top) => {
this.setState({
inspected: {
frame: {left, top, width, height},
style: props.style,
source,
},
selection: i,
// $FlowFixMe[incompatible-call] `props` from InspectorData are defined as <string, string> dictionary, which is incompatible with ViewStyleProp
setInspectedElement({
frame: {left, top, width, height},
source,
style: props.style,
});
});
}
onTouchPoint(locationX: number, locationY: number) {
this._setTouchedViewData = viewData => {
setSelectionIndex(i);
});
};
const onTouchPoint = (locationX: number, locationY: number) => {
const setTouchedViewData = (viewData: TouchedViewDataAtPoint) => {
const {
hierarchy,
props,
@@ -104,109 +105,90 @@ class Inspector extends React.Component<
// Sync the touched view with React DevTools.
// Note: This is Paper only. To support Fabric,
// DevTools needs to be updated to not rely on view tags.
const agent = this.props.reactDevToolsAgent;
if (agent) {
agent.selectNode(findNodeHandle(touchedViewTag));
if (reactDevToolsAgent) {
reactDevToolsAgent.selectNode(findNodeHandle(touchedViewTag));
if (closestInstance != null) {
agent.selectNode(closestInstance);
reactDevToolsAgent.selectNode(closestInstance);
}
}
this.setState({
panelPos:
pointerY > Dimensions.get('window').height / 2 ? 'top' : 'bottom',
selection: selectedIndex,
hierarchy,
inspected: {
style: props.style,
frame,
source,
},
setPanelPosition(
pointerY > Dimensions.get('window').height / 2 ? 'top' : 'bottom',
);
setSelectionIndex(selectedIndex);
setElementsHierarchy(hierarchy);
// $FlowFixMe[incompatible-call] `props` from InspectorData are defined as <string, string> dictionary, which is incompatible with ViewStyleProp
setInspectedElement({
frame,
source,
style: props.style,
});
};
getInspectorDataForViewAtPoint(
this.state.inspectedView,
inspectedView,
locationX,
locationY,
viewData => {
if (this._setTouchedViewData != null) {
this._setTouchedViewData(viewData);
this._setTouchedViewData = null;
}
setTouchedViewData(viewData);
return false;
},
);
}
};
setPerfing(val: boolean) {
this.setState({
perfing: val,
inspecting: false,
inspected: null,
networking: false,
});
}
const setInspecting = (enabled: boolean) => {
setSelectedTab(enabled ? 'elements-inspector' : null);
setInspectedElement(null);
};
setInspecting(val: boolean) {
this.setState({
inspecting: val,
inspected: null,
});
}
const setPerfing = (enabled: boolean) => {
setSelectedTab(enabled ? 'performance-profiling' : null);
setInspectedElement(null);
};
setTouchTargeting(val: boolean) {
const setNetworking = (enabled: boolean) => {
setSelectedTab(enabled ? 'network-profiling' : null);
setInspectedElement(null);
};
const setTouchTargeting = (val: boolean) => {
PressabilityDebug.setEnabled(val);
this.props.onRequestRerenderApp();
}
onRequestRerenderApp();
};
setNetworking(val: boolean) {
this.setState({
networking: val,
perfing: false,
inspecting: false,
inspected: null,
});
}
const panelContainerStyle =
panelPosition === 'bottom'
? {bottom: 0}
: Platform.select({ios: {top: 0}, default: {top: 0}});
render(): React.Node {
const panelContainerStyle =
this.state.panelPos === 'bottom'
? {bottom: 0}
: {top: Platform.OS === 'ios' ? 20 : 0};
return (
<View style={styles.container} pointerEvents="box-none">
{this.state.inspecting && (
<InspectorOverlay
inspected={this.state.inspected}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
onTouchPoint={this.onTouchPoint.bind(this)}
/>
)}
<View style={[styles.panelContainer, panelContainerStyle]}>
<InspectorPanel
devtoolsIsOpen={!!this.props.reactDevToolsAgent}
inspecting={this.state.inspecting}
perfing={this.state.perfing}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
setPerfing={this.setPerfing.bind(this)}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
setInspecting={this.setInspecting.bind(this)}
inspected={this.state.inspected}
hierarchy={this.state.hierarchy}
selection={this.state.selection}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
setSelection={this.setSelection.bind(this)}
touchTargeting={PressabilityDebug.isEnabled()}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
setTouchTargeting={this.setTouchTargeting.bind(this)}
networking={this.state.networking}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
setNetworking={this.setNetworking.bind(this)}
/>
</View>
return (
<View style={styles.container} pointerEvents="box-none">
{selectedTab === 'elements-inspector' && (
<InspectorOverlay
inspected={inspectedElement}
onTouchPoint={onTouchPoint}
/>
)}
<View style={[styles.panelContainer, panelContainerStyle]}>
<InspectorPanel
devtoolsIsOpen={!!reactDevToolsAgent}
inspecting={selectedTab === 'elements-inspector'}
perfing={selectedTab === 'performance-profiling'}
setPerfing={setPerfing}
setInspecting={setInspecting}
inspected={inspectedElement}
hierarchy={elementsHierarchy}
selection={selectionIndex}
setSelection={setSelection}
touchTargeting={PressabilityDebug.isEnabled()}
setTouchTargeting={setTouchTargeting}
networking={selectedTab === 'network-profiling'}
setNetworking={setNetworking}
/>
</View>
);
}
</View>
);
}
const styles = StyleSheet.create({
@@ -10,8 +10,8 @@
'use strict';
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type {PressEvent} from '../Types/CoreEventTypes';
import type {InspectedElement} from './Inspector';
const View = require('../Components/View/View');
const StyleSheet = require('../StyleSheet/StyleSheet');
@@ -19,13 +19,8 @@ const Dimensions = require('../Utilities/Dimensions').default;
const ElementBox = require('./ElementBox');
const React = require('react');
type Inspected = $ReadOnly<{|
frame?: Object,
style?: ViewStyleProp,
|}>;
type Props = $ReadOnly<{|
inspected?: Inspected,
inspected?: ?InspectedElement,
onTouchPoint: (locationX: number, locationY: number) => void,
|}>;
+3 -16
View File
@@ -10,7 +10,7 @@
'use strict';
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
import type {ElementsHierarchy, InspectedElement} from './Inspector';
import SafeAreaView from '../Components/SafeAreaView/SafeAreaView';
@@ -34,22 +34,10 @@ type Props = $ReadOnly<{|
setTouchTargeting: (val: boolean) => void,
networking: boolean,
setNetworking: (val: boolean) => void,
hierarchy?: ?Array<{|name: string|}>,
hierarchy?: ?ElementsHierarchy,
selection?: ?number,
setSelection: number => mixed,
inspected?: ?$ReadOnly<{|
style?: ?ViewStyleProp,
frame?: ?$ReadOnly<{|
top?: ?number,
left?: ?number,
width?: ?number,
height: ?number,
|}>,
source?: ?{|
fileName?: string,
lineNumber?: number,
|},
|}>,
inspected?: ?InspectedElement,
|}>;
class InspectorPanel extends React.Component<Props> {
@@ -71,7 +59,6 @@ class InspectorPanel extends React.Component<Props> {
style={this.props.inspected.style}
frame={this.props.inspected.frame}
source={this.props.inspected.source}
// $FlowFixMe[incompatible-type] : Hierarchy should be non-nullable
hierarchy={this.props.hierarchy}
selection={this.props.selection}
setSelection={this.props.setSelection}
@@ -15,6 +15,7 @@ import type {
ReactDevToolsAgent,
} from '../Types/ReactDevToolsTypes';
import type {HostRef} from './getInspectorDataForViewAtPoint';
import type {InspectedElement} from './Inspector';
import View from '../Components/View/View';
import ReactNativeFeatureFlags from '../ReactNative/ReactNativeFeatureFlags';
@@ -37,9 +38,7 @@ export default function ReactDevToolsOverlay({
inspectedView,
reactDevToolsAgent,
}: Props): React.Node {
const [inspected, setInspected] = useState<null | {
frame: {+height: any, +left: any, +top: any, +width: any},
}>(null);
const [inspected, setInspected] = useState<?InspectedElement>(null);
const [isInspecting, setIsInspecting] = useState(false);
useEffect(() => {