Integrating React Hardware with the React DevTools was a goal from the first day of the project. As mentioned in my previous article on creating a renderer, I consider interoperability with the existing ecosystem to be of utmost importance.
This mission was accomplished this past weekend with some helpful guidance from @matthewwithanm. This article will begin with a few incorrect assumptions I had about how React, React Native and the DevTools interacted and end with a couple guidelines to writing your own renderer that can interoperate with the React DevTools.
Disclaimer: these are undocumented, internal APIs. There is active work to create a first class devtool API in React. There is no guarantee that the contents of this article will be accurate at any time in the future. I personally expect that there will be a better solution in the next release or two of React.
I will attempt to use DevTools when referring to the official React DevTools, and devtools when referring to the generic concept of a developer tool.
I had two completely incorrect assumptions that led me on quite the journey in trying to get this to work.
I had assumed that the renderer must run in the browser to connect to the DevTools based on two observations.
A renderer is then responsible to look for this property and call
with a few internal modules for the DevTools to hook into. For example, you
would inject your
CustomRendererComponent into this.
The fact that each official renderer injects some internals into the DevTools
and has a different internal
Mount API should’ve been enough to inform my
brain that the devtools may be looking at or modifying these objects. Alas, it
took much trial and error and help from others and reading of code to see what
Once again, @matthewwithanm showed me that Nuclide has first-class support for React Native.
@alex_frantic mentioned that it should just work for any other renderer. I had the DevTools and React Hardware definitely connecting in some capacity, but nothing was working.
I spent a couple nights adding logs to React core, the DevTools codebase, and React Hardware to get a sense for what was happening and where what was running.
Eventually I found the following files:
For completeness, ReactDOM 0.15’s implementation is located in the source ReactDOM.js
React DevTools are doing what I like to call the duck punch monkey patch. For all the gory details take a read through the files in react-devtools/backend/integration. In there you will find test cases against each official renderer.
To work with React Devtools today you need to ensure your RendererMount interface looks like React Native, React 0.14, or React 0.15. There are a few key points:
Mount.renderComponent/_renderNewRootComponentmust return a ReactComponent
_instancesByReactRootIDcorrectly. Keep in mind you’ll likely want to clean up this object to prevent memory leaks.
Mount._instancesByReactRootIDmust exist for the devtools (I think you have an option between that interface or what React Native uses.)
In addition, you will likely be writing your own CustomRendererComponent. There is one property (that I’m currently aware of) this must hold as well:
mountComponent: must return an identifier or lookup string. React Native returns an identifier hash of sorts whilst ReactDOM returns the markup string. In React Hardware I’m simply returning the RootID which is working so far.
setupDevtools.js file from earlier? That file is responsible for
setting up a websocket to the DevTools backend. When initialized, the DevTools
As long as you have aliased your environments global object to
DevTools program can define the
__REACT_DEVTOOLS_GLOBAL_HOOK__ for a renderer
to inject into.
React Hardware runs in node, so we simply alias
window, start a
websocket server for the DevTools to connect to, and step one is complete. At
this point the DevTools and the renderer are communicating.
The next step is a bit trickier to debug and verify. Here you need to ensure that your renderer is able to get duck-punched-monkey-patched. Since this is not a first class API you won’t be in for a great time. No debugging, error, or warning messages will be printed for you. Good luck! ;)
The DevTools monkey patching wraps a few renderer methods to emit a handful of events:
I believe that this is to provide enough information to reconstruct the Render tree.
In the future these integration points will become clearer. This is unquestionably a good thing, but will also likely bring a bit of pain for any who join the custom renderer club. The following issues in React core are great to follow if you are interested in following along as these APIs become first class.
The following articles were extremely helpful in putting together a mental model of the different pieces:
Follow me on Twitter @iamdustan to learn more. I’ll be at React Conference on February 22nd and 23rd if you want to talk more about React, renderers, and internal APIs.
These words brought to you by Dustan Kasten. A friendly, bearded, husband, father, and user interface engineer living in Charlotte, NC. Considers himself quite partial to React.js these days. Find @iamdustan on Twitter