Reputation: 34820
I haven't been paying attention to the logbox errors in my React Native app for a while as I was in a rush, now I just noticed that I have a warning every time I open my app, here is what I get when I tap it:
While I know this originates from something thrown in an async function or a rejected promise, I have so many things running when app launches and I have no idea where this warning is coming from. My app seems to be working normally and I've checked all my explicit throw
s and reject
s from my code, added debugger
statements to everywhere I called them, but no avail. As seen from the screenshot, the stack trace isn't useful at all either.
How can I find the cause of unhandled promise rejection in React Native?
Upvotes: 8
Views: 1565
Reputation: 51
If your app uses Hermes, it's probably why you are not able to catch these unhandled promises as Hermes internally does some custom Promise implementation that entirely skips blocking flows or just ignores the error, when you receive unhandled promise rejections in Prod builds.
Create a file setupHermesPromiseTracking
and include it in the beginning of your index or app.tsx file (entry point file).
*/utils/setupHermesPromiseRejectionTracking.ts
declare const HermesInternal:
| {
hasPromise?: () => boolean
enablePromiseRejectionTracker?: (
options: PromiseRejectionTrackingOptions
) => void
}
| undefined
// https://github.com/facebook/react-native/blob/1954f166c9f2b22e1f3bff1a3cecddc15f9e7c31/packages/react-native/Libraries/Core/polyfillPromise.js#L25
if (HermesInternal?.hasPromise?.() === true) {
let defaultOptions: PromiseRejectionTrackingOptions | undefined
if (__DEV__) {
defaultOptions = defaultPromiseRejectionTrackingOptions
}
HermesInternal.enablePromiseRejectionTracker?.({
allRejections: true,
onUnhandled: (id, rejection) => {
defaultOptions?.onUnhandled(id, rejection)
// do what you want with the unhandled rejection
// eg: datadog.captureException(rejection)
},
onHandled: (id) => {
defaultOptions?.onHandled(id)
console.warn(`Promise rejection handled (id: ${id})`)
}
})
}
export default () => {
console.log("Hermes Promise Rejection Tracking initialized")
}
You can test it this way
// imports
import setupHermesPromiseRejectionTracking from "~/utils/setupHermesPromiseRejectionTracking"
setupHermesPromiseRejectionTracking()
const App = () => {
const testUnhandledAsyncFn = async () => {
throw new Error("This is an unhandled rejection")
}
useEffect(() => {
void testUnhandledAsyncFn() // fire the fn with no try catch
}
return (
<View>
<Text> Hello World </Text>
</View>
);
}
Upvotes: 1
Reputation: 804
In cases like this you'd rather use a library that can help you get a longer and more detailed stack trace to pin point the issue. Check out this Stackoverflow question to find some examples. In my experience Bluebird works great with react native and can produce a more extensive and helpful stack trace.
// App.tsx
import Promise from 'bluebird';
if (__DEV__) {
// replace global promise with Bluebird
global.Promise = Promise;
}
Upvotes: 2