As React applications grow in complexity, it can become increasingly difficult to keep track of components, state, and props. Last month, a few developers and I worked on building a solution to this problem. We decided to create React Scope, a Chrome extension that provides a hierarchical-tree view of a developer’s React app components.
React Scope is also interactive; as developers trigger state changes within their app (e.g., via a “click” event or form input), the tree diagram is updated accordingly. The ability to visualize state changes and identify where props are being passed down can make debugging React applications much easier.
React Scope leverages React Developer Tools to retrieve information about the client’s application. We then use this data to render a DOM tree visualization. The user simply hovers over the nodes within the tree to see each component’s name, state, and props.
Next, the developer can interact with their React application to trigger state changes. React Scope saves this data in a cache, which is connected to a listener function tracking state changes.
Finally, React Scope provides “previous” and “next” buttons. Clicking on these renders data from the cache to the tree, allowing the developer to time travel between the current and previous states of their application.
Building React Scope came with its own set of technical challenges that stretched our limits. But with challenge comes growth, and we certainly learned a substantial amount from this project.
Here were the main technical hurdles we had to solve:
We leveraged React Developer Tools’ fiber root (and renderers for React 15 and below) to obtain the client’s application data, which returned an object. Parsing this was particularly difficult since the object was heavily nested. We wrote recursive functions to extract the necessary data (component name, state, and props), which we then formatted according to D3.js’ strict format.
Obtaining data from React Developer Tools was a bit tricky due to the asynchronous relationship between iframes and the main browser window. We used Async/Await to ensure that the data being passed from the user’s application to React Scope was accurate.
Finally, we needed to determine a viable caching system to store the initial and subsequent state change data. We determined that the linear structure of a doubly linked list made the most sense. We stored the client application’s initial state as the head/tail, and added any new state data to the head. We then applied linked list logic to render relevant state data as the user clicks the “previous” and “next” buttons. For example, if they were to click on the “previous” button, our app would access the current state’s “node.prev.value” and render that data to the tree diagram.
We believe that React Scope has the potential to streamline React application development, especially with our state time traveling functionality. If a user input were to trigger an unexpected state change, for example, the developer can simply reference the previous states to determine where the error occurred.
Moreover, being able to easily visualize how props are being passed down can make debugging easier. For instance, if a prop was not accessible within a certain component, the user can navigate through the tree to identify where it failed to be passed down.
You can install React Scope from the Chrome web store. And if you have any thoughts or suggestions, please leave us feedback. This is a passion project for each of us, and we are actively exploring ways to improve it.