New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds a ReactNativeInspector API to ReactNativeRenderer #9691
Changes from 10 commits
ddfd05f
12e8367
9cb93e5
87d5fa3
179926e
6edea82
416c207
532912a
159efdb
3a3d8c3
ba9c3fa
28bef25
0f88fa5
450da60
6215bf9
d378e9c
76b721a
4ce3fc8
fc0c137
a6f8e6b
3e84ec9
e0e443e
b05ef94
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
* | ||
* @providesModule ReactNativeFiberInspector | ||
* @flow | ||
*/ | ||
'use strict'; | ||
|
||
const ReactNativeComponentTree = require('ReactNativeComponentTree'); | ||
const ReactFiberTreeReflection = require('ReactFiberTreeReflection'); | ||
const getComponentName = require('getComponentName'); | ||
|
||
import type {Fiber} from 'ReactFiber'; | ||
|
||
if (__DEV__) { | ||
var traverseOwnerTreeUp = function(hierarchy, instance: any) { | ||
if (instance) { | ||
hierarchy.unshift(instance); | ||
traverseOwnerTreeUp(hierarchy, instance._debugOwner); | ||
} | ||
}; | ||
|
||
var getOwnerHierarchy = function(instance: any) { | ||
var hierarchy = []; | ||
traverseOwnerTreeUp(hierarchy, instance); | ||
return hierarchy; | ||
}; | ||
|
||
var lastNotNativeInstance = function(hierarchy) { | ||
for (let i = hierarchy.length - 1; i > 1; i--) { | ||
const instance = hierarchy[i]; | ||
if (!instance.viewConfig) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this check would work with Fiber. Would it? |
||
return instance; | ||
} | ||
} | ||
return hierarchy[0]; | ||
}; | ||
|
||
var getHostNode = function(fiber: Fiber, findNodeHandle) { | ||
let hostNode; | ||
// look for children first for the hostNode | ||
// as composite fibers do not have a hostNode | ||
while (fiber) { | ||
hostNode = findNodeHandle(fiber.stateNode); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we first check that fiber type is either I would imagine something like: if (fiber.stateNode !== null && (fiber.type === HostComponent || fiber.type === ClassComponent)) {
hostNode = findNodeHandle(fiber.stateNode);
} I’m actually not sure if we even need to check for WDYT? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't even expose stateNode of HostComponent. That data structure will need to change and likely turn into something native. In fact, it may just be a number. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused with the check on |
||
if (hostNode) { | ||
return hostNode; | ||
} | ||
fiber = fiber.child; | ||
} | ||
return null; | ||
}; | ||
|
||
var createHierarchy = function(fiberHierarchy) { | ||
return fiberHierarchy.map(fiber => ({ | ||
name: getComponentName(fiber), | ||
getInspectorData: findNodeHandle => ({ | ||
hostNode: getHostNode(fiber, findNodeHandle), | ||
props: fiber.stateNode | ||
? getFiberCurrentPropsFromNode(fiber.stateNode) | ||
: {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not guaranteed to have the current props. It's misnamed. It's only guaranteed to have the current event handlers. |
||
source: fiber._debugSource, | ||
}), | ||
})); | ||
}; | ||
|
||
const { | ||
getClosestInstanceFromNode, | ||
getFiberCurrentPropsFromNode, | ||
} = ReactNativeComponentTree; | ||
|
||
const {findCurrentFiberUsingSlowPath} = ReactFiberTreeReflection; | ||
|
||
var getInspectorDataForViewTag = function(viewTag: number): Object { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make sure it's still defined in PROD mode (but is throwing). |
||
const fiber = findCurrentFiberUsingSlowPath( | ||
getClosestInstanceFromNode(viewTag), | ||
); | ||
const fiberHierarchy = getOwnerHierarchy(fiber); | ||
const instance = lastNotNativeInstance(fiberHierarchy); | ||
const hierarchy = createHierarchy(fiberHierarchy); | ||
const props = getFiberCurrentPropsFromNode(instance.stateNode) || {}; | ||
const source = instance._debugSource; | ||
const selection = fiberHierarchy.indexOf(instance); | ||
|
||
return { | ||
hierarchy, | ||
instance, | ||
props, | ||
selection, | ||
source, | ||
}; | ||
}; | ||
} | ||
|
||
module.exports = { | ||
getInspectorDataForViewTag, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the BSD-style license found in the | ||
* LICENSE file in the root directory of this source tree. An additional grant | ||
* of patent rights can be found in the PATENTS file in the same directory. | ||
* | ||
* @providesModule ReactNativeStackInspector | ||
* @flow | ||
*/ | ||
'use strict'; | ||
|
||
const ReactNativeComponentTree = require('ReactNativeComponentTree'); | ||
const getComponentName = require('getComponentName'); | ||
|
||
if (__DEV__) { | ||
var traverseOwnerTreeUp = function(hierarchy, instance) { | ||
if (instance) { | ||
hierarchy.unshift(instance); | ||
traverseOwnerTreeUp(hierarchy, instance._currentElement._owner); | ||
} | ||
}; | ||
|
||
var getOwnerHierarchy = function(instance) { | ||
var hierarchy = []; | ||
traverseOwnerTreeUp(hierarchy, instance); | ||
return hierarchy; | ||
}; | ||
|
||
var lastNotNativeInstance = function(hierarchy) { | ||
for (let i = hierarchy.length - 1; i > 1; i--) { | ||
const instance = hierarchy[i]; | ||
if (!instance.viewConfig) { | ||
return instance; | ||
} | ||
} | ||
return hierarchy[0]; | ||
}; | ||
|
||
var createHierarchy = function(componentHierarchy) { | ||
return componentHierarchy.map(component => ({ | ||
name: getComponentName(component), | ||
getInspectorData: () => ({ | ||
hostNode: component.getHostNode(), | ||
props: (component._instance || {}).props || {}, | ||
source: component._currentElement && component._currentElement._source, | ||
}), | ||
})); | ||
}; | ||
|
||
var getInspectorDataForViewTag = function(viewTag: any): Object { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's make sure this is defined in PROD too. |
||
const component = ReactNativeComponentTree.getClosestInstanceFromNode( | ||
viewTag, | ||
); | ||
const componentHierarchy = getOwnerHierarchy(component); | ||
const instance = lastNotNativeInstance(componentHierarchy); | ||
const hierarchy = createHierarchy(componentHierarchy); | ||
const props = (instance._instance || {}).props || {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. empty object :D |
||
const source = instance._currentElement && instance._currentElement._source; | ||
const selection = componentHierarchy.indexOf(instance); | ||
|
||
return { | ||
hierarchy, | ||
instance, | ||
props, | ||
selection, | ||
source, | ||
}; | ||
}; | ||
} | ||
|
||
module.exports = { | ||
getInspectorDataForViewTag, | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the value added by this UI is that you can inspect a quick breadcrumb - on the device. Otherwise you can just use the full DevTools.
There's nothing inherently ReactNative specific about this type of UI. Mobile web could use the same. We should build the same thing for web if it is useful to have a UI that just quickly shows the breadcrumb.
As a start, can we move this to "shared" and call it something more generic like ReactFiberBreadCrumbInspector.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this something that is going to add a lot of value to do now rather than later? I get the reasoning for doing this, but I feel having a working Fiber inspector merged soon along with RN flat bundles is going to be of more value. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree I’d prefer we do another pass at this later, but unblock RN Fiber rollout now.
(After fixing the other issues where we seem very close)