React: Provide a way for external tools to list nodes with event info

Created on 8 Dec 2016  路  8Comments  路  Source: facebook/react

I would like to add React event bubbles to the markup tree of Firefox Developer Tools. To do this I need a way to get a list of nodes and their attached event listeners.

If somebody can provide a way for me to do this it will be added within a day.

Is this possible at the moment and, if not, what can be done to make this possible?

Developer Tools Stale Feature Request

Most helpful comment

Heads up that in React 16 this hack won't work because internal property names changed.

This might work though:

function getReactListeners(node) {
  function getProps() {
    for (let key in node) {
      if (key.startsWith("__reactInternalInstance$")) {
        return node[key].memoizedProps;
      }
    }
    return null;
  }

  node = node.wrappedJSObject || node;

  let handlers = [];
  let props = getProps();

  if (props) {
    for (let name in props) {
      if (name.startsWith("on")) {
        let prop = props[name];
        let listener = prop.__reactBoundMethod || prop;

        if (typeof listener !== "function") {
          continue;
        }

        let handler = {
          type: name,
          handler: listener,
          tags: "React",
          hide: {
            dom0: true
          },
          override: {
            capturing: name.endsWith("Capture")
          }
        };

        handlers.push(handler);
      }
    }
  }

  return handlers;
}

Note that if you have onClick={cond ? foo : bar} it might give you a wrong one because __reactInternalInstance is pooled between two alternating buffers, and there's no easy way to tell which is the "current" one.

All 8 comments

I should add that I have discovered a way to do this in v15 but can't find a way to do it in anything below v15.

@spicyj: I am told that you are the best person to answer this.

If nobody is interested... closing issue.

Sorry for not replying earlier. We don't really have a public devtools API currently for inspecting the React tree which is really what you want here. It is something we'd like to add in the future though. We're actually reimplementing some of the React developer tools soon so maybe we can keep your request in mind.

cc @gaearon @sebmarkbage

Yep, being able to build the React tree would be awesome as we would have access to props and hopefully, event listeners.

For what it is worth, this is the code I am currently using to get listeners:

function getReactListeners(node) {
  function getProps() {
    for (let key in node) {
      if (key.startsWith("__reactInternalInstance$")) {
        return node[key]._currentElement.props;
      }
    }
    return null;
  }

  node = node.wrappedJSObject || node;

  let handlers = [];
  let props = getProps();

  if (props) {
    for (let name in props) {
      if (name.startsWith("on")) {
        let prop = props[name];
        let listener = prop.__reactBoundMethod || prop;

        if (typeof listener !== "function") {
          continue;
        }

        let handler = {
          type: name,
          handler: listener,
          tags: "React",
          hide: {
            dom0: true
          },
          override: {
            capturing: name.endsWith("Capture")
          }
        };

        handlers.push(handler);
      }
    }
  }

  return handlers;
}

Heads up that in React 16 this hack won't work because internal property names changed.

This might work though:

function getReactListeners(node) {
  function getProps() {
    for (let key in node) {
      if (key.startsWith("__reactInternalInstance$")) {
        return node[key].memoizedProps;
      }
    }
    return null;
  }

  node = node.wrappedJSObject || node;

  let handlers = [];
  let props = getProps();

  if (props) {
    for (let name in props) {
      if (name.startsWith("on")) {
        let prop = props[name];
        let listener = prop.__reactBoundMethod || prop;

        if (typeof listener !== "function") {
          continue;
        }

        let handler = {
          type: name,
          handler: listener,
          tags: "React",
          hide: {
            dom0: true
          },
          override: {
            capturing: name.endsWith("Capture")
          }
        };

        handlers.push(handler);
      }
    }
  }

  return handlers;
}

Note that if you have onClick={cond ? foo : bar} it might give you a wrong one because __reactInternalInstance is pooled between two alternating buffers, and there's no easy way to tell which is the "current" one.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.

Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!

Was this page helpful?
0 / 5 - 0 ratings