74

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?

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.

I would like to write code something like this in the debugging console: 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?

Daniel Williams
  • 8,003
  • 12
  • 59
  • 98
LodeRunner28
  • 1,426
  • 1
  • 12
  • 15

9 Answers9

152

Here's the helper I use: (updated to work for React <16 and 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)

Bypassing in-between components

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

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 InBetweenComp instance instead, since it's the first component ancestor of the dom-element.

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

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.

Function components

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

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 FindReact function to just: return compFiber;

Venryx
  • 8,962
  • 7
  • 50
  • 61
  • 2
    This is THE solution when patching `render` is not an option (eg enhancing a page by script injection). – dev Oct 07 '16 at 08:30
  • 1
    Awesome solution, helped me very much – Arthur Feb 16 '17 at 03:23
  • 2
    (And you can do `FindReact($0)` to get the element selected with *Right click > Inspect*) – Bloke May 02 '17 at 15:09
  • 1
    Amazing! Accomplishing what others said was impossible! – crazy2be May 20 '17 at 06:22
  • This was great! – Dean Radcliffe May 30 '18 at 21:29
  • Thanks! this solution rocks! – EyalS Nov 04 '18 at 16:51
  • This does not work with React 16 and FiberNodes, right? – Christian Dec 10 '18 at 09:19
  • @Christian Updated the code snippet to work for both React 15 and 16+ (Fiber). – Venryx Dec 21 '18 at 05:44
  • While it's working, it's kind of solution that has to be patched with every new React version. – jayarjo Feb 21 '19 at 07:13
  • I'm using React 16.8.2 and this doesn't appear to work for my root component. It returns me null in that case (although some other components it's working). I'm not sure whether it needs to be patched again or whether this solution won't work for functional components updating state with useState rather than this.state – John Vandivier Mar 08 '19 at 03:02
  • @JohnVandivier I don't use functional components, so unfortunately don't know if that's why it's not working in your case. It works everywhere I'm using it (with 16.8.3). – Venryx Mar 09 '19 at 06:15
  • 1
    it works for me from console but when i try to use the same from my chrome extension, `Object.keys` is empty for the same htmlnode. any suggestions? – Raza Ahmed May 18 '20 at 22:55
  • @RazaAhmed It may be due to the security/isolation functionality between page and extension code. Perhaps this may help: https://stackoverflow.com/a/4532567/2441655 – Venryx May 19 '20 at 00:03
  • 1
    thanks @Venryx but unfortunately it didnt work. strange that I can add/modify DOM but for this element, it is missing the react DOM properties from extension but accessible from `console` – Raza Ahmed May 19 '20 at 22:33
  • `setState` does nothing in React 16.13 – avalanche1 Feb 23 '21 at 11:52
34

Here you go. This supports 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;
};
Guan Gui
  • 535
  • 5
  • 8
  • This is the most updated working solution! One caveat though - Does not work if `el` is not the root element of the component. – Yangshun Tay May 06 '18 at 19:47
  • to me stateNode was not enough, but this worked: `el._debugOwner.stateNode.constructor.name` –  Aug 24 '18 at 09:38
  • This only returns ```{containerInfo: body, pendingChildren: null, implementation: null}``` for me. Also, constructor.name is ```Object``` – Christian Dec 10 '18 at 09:26
18

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.:

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:

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

Josh from Qaribou
  • 6,006
  • 2
  • 20
  • 21
  • Perfect. The TestUtils mechanism provided exactly what I was looking for. Now in my debug builds I can supply a global function that I can use in the console that search the returned component list and match it up to the selected element. – LodeRunner28 Mar 28 '15 at 21:45
10

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

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

$("#lol").get(0).react
Fareed Alnamrouti
  • 26,439
  • 4
  • 77
  • 71
  • this is brilliant, thanks! for example,say i used this code, and wanted to set the props of a react component,using plain javascript, how would i get react to redraw the component? eg, say i had element.react.props.config.sunhat.enabled=false; how could i then get that element to update/render? i tried element.react.render() but that didnt seem to work, any ideas? – user280109 Aug 05 '20 at 14:31
2

Here is a small snippet i'm currently using.

It works with 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)

Nadav Leshem
  • 195
  • 9
2

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.

getReactDomComponent(dom) {
  const internalInstance = dom[Object.keys(dom).find(key =>
    key.startsWith('__reactInternalInstance$'))];
  if (!internalInstance) return null;
  return internalInstance._currentElement;
}
Noah
  • 61
  • 1
  • 7
  • The ES6 version is nice and shorter. However, I'm curious, what use do you have of the "_currentElement" object itself? It doesn't have the normal functions like "setState" which most people would want to access. – Venryx Dec 27 '17 at 14:16
  • @Venryx We often use _currentElement's props attribute for test assertions. This is useful when shallow rendering isn't useful for a particular test but asserting props still has value. – Noah Dec 28 '17 at 00:00
2

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

Raza Ahmed
  • 2,461
  • 1
  • 29
  • 43
1

React 16+ version:

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's solution):

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 _debugOwner property, which is a reference to the FiberNode of the nearest component that the DOM element is part of.

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

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.

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;
    }
  }
});
Yangshun Tay
  • 33,183
  • 26
  • 102
  • 123
1

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

for 0.14.8

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

Ofcourse, its a hack only..

Shishir Arora
  • 4,257
  • 2
  • 22
  • 30