Javascript React - 从 DOM 元素获取组件以进行调试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29321742/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 03:11:00  来源:igfitidea点击:

React - getting a component from a DOM element for debugging

javascriptreactjs

提问by LodeRunner28

For the purposes of debugging in the console, is there any mechanism available in React to use a DOM element instance to get the backing React component?

为了在控制台中进行调试,React 中是否有任何可用的机制来使用 DOM 元素实例来获取支持的 React 组件?

This question has been asked previously in the context of using it in production code. However, my focus is on development builds for the purpose of debugging.

之前在生产代码中使用它的上下文中已经问过这个问题。但是,我的重点是用于调试目的的开发构建。

I'm familiar with the Chrome debugging extension for React, however this isn't available in all browsers. Combining the DOM explorer and console it is easy to use the '$0' shortcut to access information about the highlighted DOM element.

我熟悉ReactChrome 调试扩展,但并非在所有浏览器中都可用。结合 DOM 资源管理器和控制台,可以轻松使用“$0”快捷方式来访问有关突出显示的 DOM 元素的信息。

I would like to write code something like this in the debugging console: getComponentFromElement($0).props

我想在调试控制台中编写类似这样的代码:getComponentFromElement($0).props

Even in a the React development build is there no mechanism to use maybe the element's ReactId to get at the component?

即使在 React 开发构建中,是否也没有机制可以使用元素的 ReactId 来获取组件?

采纳答案by Josh from Qaribou

I've just read through the docs, and afaik none of the externally-exposed APIs will let you directly go in and find a React component by ID. However, you can update your initial React.render()call and keep the return value somewhere, e.g.:

我刚刚通读了文档,但我认为没有一个对外公开的 API 会让你直接进入并通过 ID 查找 React 组件。但是,您可以更新初始React.render()调用并将返回值保留在某处,例如:

window.searchRoot = React.render(React.createElement......

You can then reference searchRoot, and look through that directly, or traverse it using the React.addons.TestUtils. e.g. this will give you all the components:

然后,您可以引用 searchRoot,并直接查看它,或使用React.addons.TestUtils. 例如,这将为您提供所有组件:

var componentsArray = React.addons.TestUtils.findAllInRenderedTree(window.searchRoot, function() { return true; });

There are several built-in methods for filtering this tree, or you can write your own function to only return components based on some check you write.

有几种内置的方法可以过滤这棵树,或者您可以编写自己的函数以仅根据您编写的某些检查返回组件。

More about TestUtils here: https://facebook.github.io/react/docs/test-utils.html

更多关于 TestUtils 的信息:https://facebook.github.io/react/docs/test-utils.html

回答by Venryx

Here's the helper I use: (updated to work for React <16 and 16+)

这是我使用的助手:(更新为适用于 React <16 和 16+)

function FindReact(dom, traverseUp = 0) {
    const key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$"));
    const domFiber = dom[key];
    if (domFiber == null) return null;

    // react <16
    if (domFiber._currentElement) {
        let compFiber = domFiber._currentElement._owner;
        for (let i = 0; i < traverseUp; i++) {
            compFiber = compFiber._currentElement._owner;
        }
        return compFiber._instance;
    }

    // react 16+
    const GetCompFiber = fiber=>{
        //return fiber._debugOwner; // this also works, but is __DEV__ only
        let parentFiber = fiber.return;
        while (typeof parentFiber.type == "string") {
            parentFiber = parentFiber.return;
        }
        return parentFiber;
    };
    let compFiber = GetCompFiber(domFiber);
    for (let i = 0; i < traverseUp; i++) {
        compFiber = GetCompFiber(compFiber);
    }
    return compFiber.stateNode;
}

Usage:

用法:

const someElement = document.getElementById("someElement");
const myComp = FindReact(someElement);
myComp.setState({test1: test2});

Note: This version is longer than the other answers, because it contains code to traverse-up from the component directly wrapping the dom-node. (without this code, the FindReact function would fail for some common cases, as seen below)

注意:此版本比其他答案更长,因为它包含从直接包装 dom-node 的组件向上遍历的代码。(如果没有此代码,FindReact 函数将在某些常见情况下失败,如下所示)

Bypassing in-between components

绕过中间组件

Let's say the component you want to find (MyComp) looks like this:

假设您要查找的组件 ( MyComp) 如下所示:

class MyComp extends Component {
    render() {
        return (
            <InBetweenComp>
                <div id="target">Element actually rendered to dom-tree.</div>
            </InBetweenComp>
        );
    }
}

In this case, calling FindReact(target)will (by default) return the InBetweenCompinstance instead, since it's the first component ancestor of the dom-element.

在这种情况下,调用FindReact(target)将(默认情况下)返回InBetweenComp实例,因为它是 dom 元素的第一个组件祖先。

To resolve this, increase the traverseUpargument until you find the component you wanted:

要解决此问题,请增加traverseUp参数直到找到所需的组件:

const target = document.getElementById("target");
const myComp = FindReact(target, 1);   // provide traverse-up distance here

For more details on traversing the React component tree, see here.

有关遍历 React 组件树的更多详细信息,请参阅此处

Function components

功能组件

Function components don't have "instances" in the same way classes do, so you can't just modify the FindReactfunction to return an object with forceUpdate, setState, etc. on it for function components.

函数组件不像类那样具有“实例”,因此您不能仅仅修改FindReact函数以返回带有, 等的对象forceUpdatesetState用于函数组件。

That said, you can at least obtain the React-fiber node for that path, containing its props, state, and such. To do so, modify the last line of the FindReactfunction to just: return compFiber;

也就是说,您至少可以获取该路径的 React-fiber 节点,包含其道具、状态等。为此,将FindReact函数的最后一行修改为:return compFiber;

回答by Guan Gui

Here you go. This supports React 16+

干得好。这支持 React 16+

window.findReactComponent = function(el) {
  for (const key in el) {
    if (key.startsWith('__reactInternalInstance$')) {
      const fiberNode = el[key];

      return fiberNode && fiberNode.return && fiberNode.return.stateNode;
    }
  }
  return null;
};

回答by Fareed Alnamrouti

i wrote this small hack to enable access any react component from its dom node

我写了这个小技巧来允许从它的 dom 节点访问任何反应组件

var ReactDOM = require('react-dom');
(function () {
    var _render = ReactDOM.render;
    ReactDOM.render = function () {
        return arguments[1].react = _render.apply(this, arguments);
    };
})();

then you can access any component directly using:

然后您可以使用以下命令直接访问任何组件:

document.getElementById("lol").react

or using JQuery

或使用 JQuery

$("#lol").get(0).react

回答by Nadav Leshem

Here is a small snippet i'm currently using.

这是我目前正在使用的一个小片段。

It works with React 0.14.7.

它适用于 React 0.14.7。

Gist with the code

代码要点

let searchRoot = ReactDom.render(ROOT, document.getElementById('main'));

var getComponent = (comp) => comp._renderedComponent ? getComponent(comp._renderedComponent) : comp;

var getComponentById = (id)=> {
  var comp = searchRoot._reactInternalInstance;
  var path = id.substr(1).split('.').map(a=> '.' + a);
  if (comp._rootNodeID !== path.shift()) throw 'Unknown root';
  while (path.length > 0) {
    comp = getComponent(comp)._renderedChildren[path.shift()];
  }
  return comp._instance;
};

window.$r = (node)=> getComponentById(node.getAttribute('data-reactid'))

to run it, open Devtools, highlight an element you want to examine, and in the console type : $r($0)

要运行它,打开 Devtools,突出显示要检查的元素,然后在控制台中键入: $r($0)

回答by Noah

I've adapted @Venryx's answer with a slightly adapted ES6 version that fit my needs. This helper function returns the current element instead of the _owner._instance property.

我已经使用适合我需要的略微调整的 ES6 版本调整了@Venryx 的答案。此辅助函数返回当前元素而不是 _owner._instance 属性。

getReactDomComponent(dom) {
  const internalInstance = dom[Object.keys(dom).find(key =>
    key.startsWith('__reactInternalInstance$'))];
  if (!internalInstance) return null;
  return internalInstance._currentElement;
}

回答by Yangshun Tay

React 16+ version:

反应 16+ 版本:

If you want the nearest React component instance that the selected DOM element belongs to, here's how you can find it (modified from @Guan-Gui'ssolution):

如果您想要所选 DOM 元素所属的最近 React 组件实例,您可以通过以下方法找到它(从@Guan-Gui 的解决方案中修改):

window.getComponentFromElement = function(el) {
  for (const key in el) {
    if (key.startsWith('__reactInternalInstance$')) {
      const fiberNode = el[key];
      return fiberNode && fiberNode._debugOwner && fiberNode._debugOwner.stateNode;
    }
  }
  return null;
};

They trick here is to use the _debugOwnerproperty, which is a reference to the FiberNodeof the nearest component that the DOM element is part of.

他们在这里的技巧是使用_debugOwner属性,它是FiberNode对 DOM 元素所属的最近组件的引用。

Caveat: Only running in dev mode will the components have the _debugOwnerproperty. This would not work in production mode.

警告:只有在开发模式下运行,组件才会具有该_debugOwner属性。这在生产模式下不起作用。

Bonus

奖金

I created this handy snippet that you can run in your console so that you can click on any element and get the React component instance it belongs to.

我创建了这个方便的片段,您可以在控制台中运行它,以便您可以单击任何元素并获取它所属的 React 组件实例。

document.addEventListener('click', function(event) {
  const el = event.target;
  for (const key in el) {
    if (key.startsWith('__reactInternalInstance$')) {
      const fiberNode = el[key];
      const component = fiberNode && fiberNode._debugOwner;
      if (component) {
        console.log(component.type.displayName || component.type.name);
        window.$r = component.stateNode;
      }
      return;
    }
  }
});

回答by Shishir Arora

Install React devtools and use following, to access react element of corresponding dom node ($0).

安装 React devtools 并使用以下方法访问对应 dom 节点 ($0) 的 react 元素。

for 0.14.8

0.14.8

    var findReactNode = (node) =>Object.values(__REACT_DEVTOOLS_GLOBAL_HOOK__.helpers)[0]
.getReactElementFromNative(node)
._currentElement;
       findReactNode(
/* Node extends text, svg, html
 usage for node ##代码##:
    ##代码##.reactive // returns [node, parentNode, rootNode]
    ##代码##.react.props // {any:'prop'}
    ##代码##.react.setState(...) // update
 */
Object.defineProperties(Node.prototype, {
    _react: {writable:true, value:''}
    ,reactKey: {
        get: function(){
            let symbol = this._react;
            if(symbol){ return symbol; }
            // v15, v16 use a string as key, probably a real symbol in the future
            symbol = Object.keys(this).find(key => key.startsWith('__reactInternalInstance$'));
            return Node.prototype._react = symbol || '';
        }
    }
    // try to find the props/state/React-instance
    ,react: {
        get: function(){
            let react = this[ this.reactKey ] || null;
            let ##代码##;
            if(react){
                ##代码## = react._currentElement;
                if(##代码##){ // v15
                    if(##代码##._owner){
                        return ##代码##._owner._instance;
                    }else{
                        return ##代码##;
                    };
                }
                ##代码## = react.return;
                if(##代码##){ // v16
                    // develop mode only: return react._debugOwner.stateNode
                    // both develop and prod modes:
                    return ##代码##.stateNode
                }
            }else if(this._reactRootContainer){
                // v16 _internalRoot === _internalRoot.current.stateNode
                return this._reactRootContainer._internalRoot;
            }
            return react;
        }
    }
    // make a list of self, ancestors that make up this branch of the tree
    ,reactive: {
        get: function(list=[]){
            let ##代码## = this;
            while(##代码## && !##代码##[ ##代码##.reactKey ] && !##代码##._reactRootContainer ){
                ##代码## = ##代码##.previousSibling;
            };
            if(##代码## && (##代码##[##代码##.reactKey] || ##代码##._reactRootContainer)){
                list.push(##代码##);
            };
            ##代码## = this;
            while(##代码## = ##代码##.parentNode){
                if(##代码##[ ##代码##.reactKey ] || ##代码##._reactRootContainer){
                    list.push(##代码##);
                }
            };
            return list;
        }
    }
});
);

Ofcourse, its a hack only..

当然,它只是一个黑客..

回答by jimmont

v15 and v16 compatible with svg, html, comment, text nodes

v15 和 v16 兼容 svg、html、comment、text 节点

##代码##

回答by Raza Ahmed

In case someone is struggling like me to access React component/properties from a chrome extension, all of the above solutions are not going to work from chrome extension content-script. Rather, you'll have to inject a script tag and run your code from there. Here is complete explanation: https://stackoverflow.com/a/9517879/2037323

如果有人像我一样努力从 chrome 扩展访问 React 组件/属性,上述所有解决方案都不适用于 chrome 扩展内容脚本。相反,您必须注入一个脚本标记并从那里运行您的代码。这里是完整的解释:https: //stackoverflow.com/a/9517879/2037323