Contrary to popular belief, there are more efficient ways of debugging than just putting console.log all over your code (or at least that's what I've heard). There are plenty of tools and libraries that can improve the debugging experience. Plus, the Expo community, as well as the greater React and React Native communities, are great resources for help. There's a good chance someone else has run into the exact same error as you, so make sure to read the documentation, search the Expo forums, Github, and StackOverflow.
In development, you'll either see Redbox errors, or Yellowbox warnings. Redbox errors will show when a fatal error has occurred that prevents your app from running. Warnings will show to let you know of a possible issue that you should probably look into before shipping your app.
You can also create warnings and errors on your own (commonly referred to as "throwing" an error) via console.warn("Warning message") and console.error("Error message"). Or, you can also just throw Error("Error message").
Redbox Errors and the Stack Trace
When you encounter an error during development, you will be shown the error message, as well as the "stacktrace," which is a report of the recent calls your application made or was making when it crashed. This stacktrace is shown both in your terminal, as well as on the simulator or device you're running your project on. When you run your app it is transformed through Babel and your code will no longer look the same as it does in your editor.
This stacktrace is extremely valuable since it gives you the location the error comes from. For example, in the following clip we know that the error came from the file LinksScreen.js on line 10 and column (character) 15.
This menu gives you access to several functions which are useful for debugging.
It is also known as the Debug Menu.
Invoking it depends on the device where you are running your application:
iOS Device: Shake the device a little bit.
iOS Simulator: Hit Ctrl-Cmd-Z on a Mac in the emulator to simulate the shake gesture, or press Cmd+D.
Android Device: Shake the device vertically a little bit.
Android Emulator: Either hit Cmd+M, or run adb shell input keyevent 82 in your terminal window.
The Developer Menu gives you a couple different functionalities. A few are pretty self-explanatory, like:
Reload manifest & JS bundle: this will reload your app. Usually this isn't necessary if you have Live or Hot Reload enabled, since it will automatically refresh whenever you save your changes in your text editor.
Go to Expo Home: Leave your app and navigate back to the Expo Client homescreen
Enable/Disable Live Reload: When enabled, your app will automatically refresh the JS bundle whenever you save file changes in your project directory.
Note: In order to use Live Reload, your components must be class components, rather than a functional components. You can read about their differences here.
Now let's explore some of the more exciting functionalities...
Show Performance Monitor
This opens up a small window giving you performance information of your app. You will see:
RAM usage of your project
2 numbers for Views, the top indicates the number of views for the screen, the bottom indicates the number of views in the component
Frames Per Second for the UI and JS threads. The UI thread is used for native Android or iOS UI rendering. The JS thread is where most of your logic will be run, including API calls, touch events, etc.
Toggle Element Inspector & Debug Remote JS
Both of these work best in tandem with either react-native-debugger or react-devtools. Read on to see more!
React Native Debugger
The React Native Debugger includes a lot of the tools listed later in this page, all bundled into one, including React-DevTools (guide below) and network request inspection. For this reason, if you use one tool on this page, it should probably be this one!
We'll give a quick look at it here, but check out their documentation for a more in-depth look.
You can install it via the release page, or if you're on a mac just use:
brew cask install react-native-debugger
To start up your project and start react-native-debugger at the same time, navigate to your project's root directory and run
REACT_DEBUGGER="unset ELECTRON_RUN_AS_NODE && open -g 'rndebugger://set-debugger-loc?port=19001' ||" expo start
This automatically starts your app in "Remote Debugging" mode, and will launch the React Native Debugger console. In this console, you can see the Element tree, the props, state, and children of whatever element you select. You also have the Chrome console on the right, and if you type $r in the console, you will see the breakdown of your selected element.
If you right-click anywhere in the React Native Debugger, you'll get some handy short-cuts to reload your JS, enable/disable the element inspector, network inspector, and to log and clear your AsyncStorage content.
It's easy to use the React Native Debugger to debug your network requests. Simple right-click to Enable Network Inspect, which allows you to open the Network tab and inspect requests of fetch and XMLHttpRequest. There are some limitations, so there are a few other alternatives, all require using a proxy. The following options will all work:
On Android, the Proxy Settings app is helpful for switch between debug and non-debug mode. Unfortunately it doesn't work with Android M yet.
Redux is a popular library for managing the state of your app that doesn't belong to any single component, and instead it shared throughout the app. You can use the React Native Debugger (told you this tool does it all), the set up is as follows:
Open the app, press ⌘+t/ctrl+t to open new window, then set the port to 19001.
Start your app, open the in-app developer menu, and select “Debug JS Remotely.”
Configure __REDUX_DEVTOOLS_EXTENSION__ as shown here.
You're now good to go! If you are experiencing any issues or want to learn more about how to use these tools, refer to this guide.
Debugging with React DevTools
React DevTools is a great way to get a look at each of your components' props and state. First, you'll need to run
npm install -g react-devtools@^3
(if you don't want to install it globally, you can just run npm install --dev react-devtools@^3 to install it as a project dependency).
Then, after running expo start in your project's root directory, in a separate terminal tab run react-devtools. This will open up the React Devtools console (for it to connect, just refresh the project via the Developer Menu). From this console, you can search for your React components at the top, or open up the Developer Menu and enable the Element Inspector. Once you do that, you can tap on any element on screen and React DevTools will automatically find and display that element in the tree. From there, you can inspect the elements state, props, etc.
React DevTools can also be paired with remote debugging, allowing you to inspect props, state, and instance properties in the Chrome console. If you have any questions on setting that up, give the next section a look!
Remote Debugging with Chrome Developer Tools
To ensure the best debugging experience, first change your host type in Expo Dev Tools to LAN or localhost. If you use Tunnel with debugging enabled, you are likely to experience so much latency that your app is unusable. While here, also ensure that Development Mode is checked.
If you are using LAN, make sure your device is on the same wifi network as your development machine. This may not work on some public networks. localhost will not work for iOS unless you are in the simulator, and it only work on Android if your device is connected to your machine via usb.
Line numbers for console.log statements don't work by default when using Chrome debugging. To get correct line numbers open up the Chrome Dev Tools settings, go to the "Blackboxing" tab, make sure that "Blackbox content scripts" is checked, and add expo/build/logs/RemoteConsole.js as a pattern with "Blackbox" selected.
Troubleshooting localhost debugging
When you start a project with Expo CLI and when you press Run on Android device/emulator in Expo Dev Tools (or a in the terminal), Expo CLI will automatically tell your device to forward localhost:19000 and 19001 to your development machine, as long as your device is plugged in or emulator is running. If you are using localhost for debugging and it isn't working, close the app and open it up again using Open on Android. Alternatively, you can manually forward the ports using the following command if you have the Android developer tools installed: adb reverse tcp:19000 tcp:19000 - adb reverse tcp:19001 tcp:19001
Source maps and async functions
Source maps and async functions aren't 100% reliable. React Native doesn't play well with Chrome's source mapping in every case, so if you want to make sure you're breakpointing in the correct place, you should use the debugger call directly from your code.
In a perfect world, your app would ship without any bugs. However, that's usually not the case. So, it's usually a good idea to implement a crash and bug reporting system into your app. This way, if any user experiences a fatal JS error (or any event that you've configured to notify Sentry) you can see the details in your Sentry dashboard.
Expo provides a wrapper called sentry-expo which allows you to get as much information as possible from crashes and other events. Plus, when running in the managed workflow, you can configure sourcemaps so that the stracktraces you see in Sentry will look much more like the code in your editor.