bvaughn/react-devtools-experimental

React Native: Standalone package and basic RN support

bvaughn opened this issue · 3 comments

Need to create react-devtools and react-devtools-core packages to support React Native, Safari, iframes, and other embedded cases like Nuclide and Flipper.

  • Test standalone shell with Safari
  • Test standalone shell with iOS React Native (with and without v8)
  • Test standalone shell with Android React Native
  • Test embedded integration with Flipper

Some features will require changes to support the RN environment:

  • Component filters will need to be saved somewhere between reloads. (Perhaps they will need to be sent eagerly on connect, similar to how we do for reload-and-profile in the browser.)
  • Inspecting an element in React Native should also select it in DevTools.
  • The selection tool in DevTools should enable clicking on a native view in React Native to select its owner.
  • Overlay/highlight component to show the currently-selected component. (Currently this is just disabled for RN.)
  • Determine if it would be possible to support reload-and-profile functionality for React Native.
  • Don't hide the DevTools app if Profiler has data when an app unmounts.
  • React Native style editor plug-in. (Maybe we can use view config as a whitelist of all valid prop names?)
  • Fabric support (stretch goal)
  • Special tag/badge for native components (React Native will be dropping the "RCT" prefix soon).
  • Inspect component in IDE (launch editor) feature.

In addition, because the v4 backend API is incompatible with v3, releasing the new react-devtools and react-devtools-core packages will also require updating and coordinating with: React Native, Flipper, and Nuclide packages (and possibly others).

For now, I will be pushing my work to the npm-packages branch. I will rebase and cleanup before merging.

Hi @bvaughn, do you have any work about the update for Inspector.js? Currently it isn't working with the global hook of v4.

@jhen0409 Yes, I have a patch to a few React Native components. Inspector and several of the YellowBox* components. Here's the Inspector patch 😄

33c33,34
< // required for devtools to be able to edit react native styles
---
> // Required for React DevTools to view/edit React Native styles in Flipper.
> // Flipper doesn't inject these values when initializing DevTools.
34a36,39
> const viewConfig = require('../Components/View/ReactNativeViewViewConfig.js');
> hook.nativeStyleEditorValidAttributes = Object.keys(
>   viewConfig.validAttributes.style,
> );
37,39c42
<   const allRenderers = Object.keys(hook._renderers).map(
<     key => hook._renderers[key],
<   );
---
>   const allRenderers = Array.from(hook.renderers.values());
80a84
>   _hideTimeoutID: TimeoutID | null = null;
119,126c123,125
<     let _hideWait = null;
<     const hlSub = agent.sub('highlight', ({node, name, props}) => {
<       clearTimeout(_hideWait);
< 
<       if (typeof node !== 'number') {
<         // Fiber
<         node = ReactNative.findNodeHandle(node);
<       }
---
>     agent.addListener('hideNativeHighlight', this._onAgentHideNativeHighlight);
>     agent.addListener('showNativeHighlight', this._onAgentShowNativeHighlight);
>     agent.addListener('shutdown', this._onAgentShutdown);
128,136c127,128
<       UIManager.measure(node, (x, y, width, height, left, top) => {
<         this.setState({
<           hierarchy: [],
<           inspected: {
<             frame: {left, top, width, height},
<             style: props ? props.style : {},
<           },
<         });
<       });
---
>     this.setState({
>       devtoolsAgent: agent,
138,147c130,157
<     const hideSub = agent.sub('hideHighlight', () => {
<       if (this.state.inspected === null) {
<         return;
<       }
<       // we wait to actually hide in order to avoid flicker
<       _hideWait = setTimeout(() => {
<         this.setState({
<           inspected: null,
<         });
<       }, 100);
---
>   };
> 
>   _onAgentHideNativeHighlight = () => {
>     if (this.state.inspected === null) {
>       return;
>     }
>     // we wait to actually hide in order to avoid flicker
>     this._hideTimeoutID = setTimeout(() => {
>       this.setState({
>         inspected: null,
>       });
>     }, 100);
>   };
> 
>   _onAgentShowNativeHighlight = node => {
>     clearTimeout(this._hideTimeoutID);
> 
>     if (typeof node !== 'number') {
>       node = ReactNative.findNodeHandle(node);
>     }
> 
>     UIManager.measure(node, (x, y, width, height, left, top) => {
>       this.setState({
>         hierarchy: [],
>         inspected: {
>           frame: {left, top, width, height},
>         },
>       });
149c159,172
<     this._subs = [hlSub, hideSub];
---
>   };
> 
>   _onAgentShutdown = () => {
>     const agent = this.state.devtoolsAgent;
>     if (agent != null) {
>       agent.removeListener(
>         'hideNativeHighlight',
>         this._onAgentHideNativeHighlight,
>       );
>       agent.removeListener(
>         'showNativeHighlight',
>         this._onAgentShowNativeHighlight,
>       );
>       agent.removeListener('shutdown', this._onAgentShutdown);
151d173
<     agent.on('shutdown', () => {
153,157c175
<       this._subs = null;
<     });
<     this.setState({
<       devtoolsAgent: agent,
<     });
---
>     }
190,194c208
<       this.state.devtoolsAgent.selectFromDOMNode(
<         touchedViewTag,
<         true,
<         offsetFromLeaf,
<       );
---
>       this.state.devtoolsAgent.selectNode(touchedViewTag);

I will be landing support for this today (hopefully) within Facebook. Going to close this out for now.