Knowing how to effectively debug an application is of the utmost importance to frontend developers as they’ll be spending a good portion of their time reproducing and fixing issues. Besides, what better way of deeply understanding a language or framework if not by looking at its inner workings.
In this article, we’ll be covering some of the tactics and tools ReactJS programmers use on a daily basis, not only in their very predictable development environment, but also in production when their code comes to life and things may — and will — go haywire.
(Most of the learnings here are applicable to all frontend applications)
React debugging tools
One of the popular extensions out there is definitely the React Developer Tools available on both Firefox and Chrome. It makes it easy to inspect and edit the props/state of the root React components rendered on the page and record performance information.
How to debug with Chrome DevTools
Let’s hit F12 and get familiar with the different panels.
The Console serves two objectives at least: display of your application’s logs plus runtime errors and ability to execute custom JS code.
Developers may be logging messages using
console.log() to inspect the value of some variable or simply ensure their code is executing in the right order. Warnings and the majority of runtime errors get also printed here. See
console JS object to learn what you can do with it.
On top of that, your Console is a REPL environment and has full access to
This where your entire application’s network activity will be logged. All resources, whether downloaded or uploaded, are available for inspection in the Network panel. These resources are listed chronologically with information such as HTTP response code, loading time, initiator, state and size. The footer contains a summary of the total number of requests and the size of all resources.
This list view helps easily spot requests that failed (the red ones) and those that are pending and may soon timeout. If you’re looking for CORS issues, it’s better to start from Console then eventually switch to Network (these requests appear in white, as successful).
Clicking on a specific resource opens a detailed panel where HTTP Headers are shown, alongside the Response and even an HTML Preview of it. It’s in fact easier to debug the received data or simply inspect images here than in your HTML source code.
And finally the Timing breakdown which can prove very useful when investigating requests that take a long time to download, either due to a poor connection between your React application and the server (consider using a CDN if Time To First Byte is high) or to a slow backend if that server is hosted locally.
Tips: Hit Preserve log to keep the logs when navigating from one page to another, and check Disable cache so resources won’t be served from cache when DevTools is open.
This is by far the most powerful tool available in DevTools, and you can find it under the Sources tab.
This is a faster and cleaner alternative to using
console.log() when looking for bugs. It allows for quick inspection of the piece of code being executed and the values of all variables at that moment in time.
The execution comes to a halt as soon as it reaches the breakpoint. And you can set many of them at the same time. From there you can deep dive into your code using the different options at hand:
- Step through the code by resuming execution, one line at a time, or hop on to the next function call.
- Check the call stack and see what functions are called and their order.
- Scope pane is where all your local, global and closure (when applicable) variables get printed. You can change their values and see how it affects your application upon resuming execution.
- Watch a specific variable by tracking its value over time. You can even use JS expressions here.
- Console has access to the application state, use it to change values and to keep an eye on your
Other types of breakpoints are available:
- DOM — when a node or its children gets changed or removed.
- XHR — triggered when the requested URL contains a specific string.
- Event Listener — pauses on the code that runs right after an event is fired, say
clickor specific categories of events (all mouse related).
Who doesn’t hate losing changes whenever you hit that reload button? Local Overrides were made to overcome this. Specify a directory in
Sources -> Overrides and let DevTools save your changes. It works in most situations (see limitations).
Store Specific Tools
DevTools might not be enough whenever you need to deep dive into some specific issues related to your store. Here are some of the tools you may use for Redux and MobX.
- redux-logger — for keeping track of your redux actions and state changes.
- redux-devtools — this comes a browser extension or a library and provides power-ups for redux related issues, including the ability to export the store in JSON format.
- redux-immutable-state-invariant — this middleware is for development use only and helps with errors related to mutations.
- mobx-logger — for logging actions, reactions, transactions and computations.
- mobx-devtools — available as a browser extension for inspecting the mobx-state-tree and observables.
HTML Structure & CSS
The Element tab is where you do all your DOM related activities. It allows you to edit the DOM and CSS on the fly.
You can inspect any particular DOM node, change its content, attribute or type and force its state (like
:hover). You can remove nodes or reorder your tree by drag and dropping them and see how it renders.
On the right side of the panel you can see the CSS styles that are applied to the selected DOM element. DevTools lets you inspect styles in different ways by editing them, or by adding new styles and rules. The box at the bottom helps with the margins, paddings, dimensions and border.
Make sure to persist your changes to the development workspace if you don’t want them to disappear upon the next reload.
DevTools offers several ways (across various panels) to audit and improve the performance of your React application.
The Audits tab is where you can look for bottlenecks hurting your page’s loading speed. Open your application in incognito mode as some browser plugins may interfere with the auditing workflow.
Start with an audit as it creates a baseline to measure your changes against, but also gives you some actionable tips on how to improve. The available options let you run different categories of audits: SEO, accessibility or performance oriented. It also helps simulate how your application behaves on a mobile device (viewport and bandwidth/CPU throttling).
Results show the overall score (the higher the better) and a set of different sections:
- Metrics — laying out the different aspects of the loading experience, such as when the content is displayed (First Contentful Paint) or when page is ready for user interactions (Time To Interactive).
- Opportunities — with suggestions on how to speed up the page.
- Diagnostics — full of details on how to improve performance.
Tip: Isolate your changes by making one improvement at a time, before rerunning an audit for measuring how it affected performance.
Memory issues are obvious and oftentimes noticed by your users as your application will tend to either pause (GarbageCollector kicks in more frequently than it should) or becomes slower throughout the session (think memory leak).
A dedicated Memory tab helps investigate these issues. Track down memory leaks by monitoring your JS heap (in Allocation timelines) and by finding detached DOM nodes — those removed from the tree but still being referenced by some JS — using Heap snapshots. You can even dissect memory allocation by function. Learn more about this topic here.
Learn more about memory problems.
Frames per second
FPS is captured alongside other metrics by DevTools at execution time. It is useful for spotting slow animations that hurt your user experience. Low framerates translate in red bars on the chart.
DevTools also offers an FPS meter, producing real-time estimations at runtime, and CPU throttling for simulation on mobile devices.
How to debug Production issues?
Issues reported by users do often lack the full context to have a complete understanding of what happened. “X doesn’t work” doesn’t obviously help you. Not to mention the time it takes to collect clues from your users with the hope of being able to reproduce the problem locally. Besides, how many of them would bother to submit a ticket or share a bad experience? An extremely tiny fraction I guess.
So don’t go easy on logs. Anything that helps you make sense of a bug is useful. Below are a few tips on what’s worth tracking.
Monitor your XHR
Make sure to log the XHR requests that failed (5xx/4xx) and their responses. Most HTTP libraries will allow you to easily handle such cases.
Catch your JS errors
Runtime errors are critical for diagnosing issues. They are worth a thousand log entries and provide an accurate picture of the problem. Store them on your server for later retrieval. If you had to log only one thing, start with JS errors.
Track your app’s State and Actions
Whether using Redux, MobX or any other store, take the time to log your application’s state changes and actions. You can even replay them in your browser, if you’re on Redux, with redux-slider-monitor. This would come in handy for thorough debugging of production issues.
Record your user actions
Logging user actions and journeys is a powerful, yet underused technique in debugging. Such strategy is widely used by marketers who track down each and every frustration that may affect the overall user experience, ranging from rage clicks to incorrect rendering of certain elements in the web app. Tools like Mixpanel, Amplitude or Heap haven’t been designed for developers and therefore rarely used by them. But this is not an excuse for not simply logging your user steps after sanitizing any residual private data.
In fact, being able to mentally visualize your user journey — while they experience an issue in production — and at the same time see how that affected your React stack is simply HUGE. It’s the ultimate root cause analysis technique and a serious productivity booster.
Monitor React apps in Production
Debugging a React application in production may be challenging and time consuming. Try using a unified monitoring platform that gathers all you need for troubleshooting in one single place. Try Asayer for free.
Asayer is a frontend monitoring tool that replays everything your users do and shows how your React app behaves for every issue. It’s like having your browser’s inspector open while looking over your user’s shoulder.
Asayer lets you reproduce issues, aggregate JS errors and monitor your app’s performance. Asayer offers plugins for capturing the state and actions of your Redux/MobX stores and for inspecting GraphQL queries.
Happy debugging, for modern frontend teams - Start monitoring your React app for free.