React - getting a component from a DOM element for debugging

JavascriptReactjs

Javascript Problem Overview


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 https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en">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?

Javascript Solutions


Solution 1 - Javascript

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=>{
		return key.startsWith("__reactFiber$") // react 17+
			|| key.startsWith("__reactInternalInstance$"); // react <17
	});
	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;

Solution 2 - Javascript

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

Solution 3 - Javascript

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

Solution 4 - Javascript

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

Solution 5 - Javascript

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

Solution 6 - Javascript

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)

Solution 7 - Javascript

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

Solution 8 - Javascript

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

Solution 9 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLodeRunner28View Question on Stackoverflow
Solution 1 - JavascriptVenryxView Answer on Stackoverflow
Solution 2 - JavascriptGuan GuiView Answer on Stackoverflow
Solution 3 - JavascriptJosh from QaribouView Answer on Stackoverflow
Solution 4 - JavascriptFareed AlnamroutiView Answer on Stackoverflow
Solution 5 - JavascriptRaza AhmedView Answer on Stackoverflow
Solution 6 - JavascriptNadav LeshemView Answer on Stackoverflow
Solution 7 - JavascriptNoahView Answer on Stackoverflow
Solution 8 - JavascriptYangshun TayView Answer on Stackoverflow
Solution 9 - JavascriptShishir AroraView Answer on Stackoverflow