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

时间:2015-03-28 19:44:31

标签: javascript reactjs

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

此问题之前已在生产代码中使用它的上下文中提出过。但是,我的重点是用于调试的开发版本。

我熟悉Chrome debugging extension for React,但并非所有浏览器都提供此功能。结合DOM资源管理器和控制台,可以很容易地使用“$ 0”快捷方式访问有关突出显示的DOM元素的信息。

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

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

10 个答案:

答案 0 :(得分:97)

这是我使用的:(更新为React< 16和16 +工作)

window.FindReact = function(dom) {
    let key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$"));
    let internalInstance = dom[key];
    if (internalInstance == null) return null;

    if (internalInstance.return) { // react 16+
        return internalInstance._debugOwner
            ? internalInstance._debugOwner.stateNode
            : internalInstance.return.stateNode;
    } else { // react <16
        return internalInstance._currentElement._owner._instance;
    }
}

然后使用它:

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

答案 1 :(得分:22)

你走了。这支持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;
};

答案 2 :(得分:15)

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

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

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

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

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

有关TestUtils的更多信息,请访问:https://facebook.github.io/react/docs/test-utils.html

答案 3 :(得分:5)

我写了这个小黑客来启用从其dom节点访问任何反应组件

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

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

document.getElementById("lol").react

或使用JQuery

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

答案 4 :(得分:2)

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

适用于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'))

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

答案 5 :(得分:2)

React 16+版本:

如果您想要所选DOM组件所属的最近的React组件实例,请按照以下方法找到它(从@Guan-Gui's解决方案修改):

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;
};

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

警告:只有在开发模式下运行才能使组件具有_debugOwner属性。这在生产模式下无效。

加成

我创建了这个方便的代码片段,您可以在控制台中运行,以便您可以单击任何元素并获取它所属的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;
    }
  }
});

答案 6 :(得分:2)

安装React devtools并使用以下命令来访问相应dom节点的反应元素($ 0)。

为0.14.8

    var findReactNode = (node) =>Object.values(__REACT_DEVTOOLS_GLOBAL_HOOK__.helpers)[0]
.getReactElementFromNative(node)
._currentElement;
       findReactNode($0);

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

答案 7 :(得分:1)

我已经根据我的需要改编了@Venryx的答案,略微适应了ES6版本。此帮助函数返回当前元素而不是_owner._instance属性。

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

答案 8 :(得分:0)

v15和v16与svg,html,注释,文本节点兼容

/* Node extends text, svg, html
 usage for node $0:
    $0.reactive // returns [node, parentNode, rootNode]
    $0.react.props // {any:'prop'}
    $0.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 $0;
            if(react){
                $0 = react._currentElement;
                if($0){ // v15
                    if($0._owner){
                        return $0._owner._instance;
                    }else{
                        return $0;
                    };
                }
                $0 = react.return;
                if($0){ // v16
                    // develop mode only: return react._debugOwner.stateNode
                    // both develop and prod modes:
                    return $0.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 $0 = this;
            while($0 && !$0[ $0.reactKey ] && !$0._reactRootContainer ){
                $0 = $0.previousSibling;
            };
            if($0 && ($0[$0.reactKey] || $0._reactRootContainer)){
                list.push($0);
            };
            $0 = this;
            while($0 = $0.parentNode){
                if($0[ $0.reactKey ] || $0._reactRootContainer){
                    list.push($0);
                }
            };
            return list;
        }
    }
});

答案 9 :(得分:0)

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