I am trying to set up expo-notifications on an existing react-native project that has recently been upgraded to expo 49.0.5. I have created a custom hook to handle all my notification logic.
However as soon as I run the app using the Expo Go app on my iPad, after it finishes compiling, I get the following error:
TypeError: Cannot read property 'custom' of undefined, js engine: hermes
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:105:15 in reportException
at node_modules/react-native/Libraries/Core/ExceptionsManager.js:150:4 in handleException
at node_modules/react-native/Libraries/Core/setUpErrorHandling.js:26:18 in handleError
at node_modules/expo/build/errors/ExpoErrorManager.js:27:16 in errorHandler
at node_modules/expo/build/errors/ExpoErrorManager.js:32:8 in anonymous
at node_modules/@react-native/js-polyfills/error-guard.js:51:16 in reportFatalError
at node_modules/metro-runtime/src/polyfills/require.js:237:19 in guardedLoadModule
at http://192.168.1.246:19000/index.bundle?platform=ios&dev=true&hot=false&strict=false&minify=false:null in metroRequire
at http://192.168.1.246:19000/index.bundle?platform=ios&dev=true&hot=false&strict=false&minify=false:null in global
I have been able narrow down the cause of the error is when I add this import statement
import * as Notifications from "expo-notifications
So I decided to try creating a brand new expo app with the same dependencies, and that worked! But the problem is I need it on this existing project...
Here is the custom hook I am using:
import { useState, useEffect, useRef, useCallback } from "react";
//notifications
import * as Device from "expo-device";
import * as Notifications from "expo-notifications";
// init notification handler
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
const useNotification = () => {
const [notification, setNotification] = useState(false);
const [expoPushToken, setExpoPushToken] = useState("");
const notificationListener = useRef();
const responseListener = useRef();
useEffect(() => {
registerForPushNotificationsAsync().then((token) => {
console.log("token", token)
alert(token);
setExpoPushToken(token)
});
notificationListener.current =
Notifications.addNotificationReceivedListener((notification) => {
console.log("got a notificiation", notification)
setNotification(notification);
});
responseListener.current =
Notifications.addNotificationResponseReceivedListener((response) => {
console.log(response);
});
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
return { expoPushToken, notification };
};
export default useNotification;
export async function schedulePushNotification() {
await Notifications.scheduleNotificationAsync({
content: {
title: "You've got mail! ",
body: "Here is the notification body",
data: { data: "goes here" },
},
trigger: { seconds: 2 },
});
}
async function registerForPushNotificationsAsync() {
let token;
if (Platform.OS === "android") {
await Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
if (Device.isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
console.log(token);
} else {
alert("Must use physical device for Push Notifications");
}
return token;
}
Here is my package.json
{
"scripts": {
"start": "expo start --dev-client",
"android": "expo run:android",
"ios": "expo run:ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"@expo/config-plugins": "~7.2.2",
"@gorhom/bottom-sheet": "^4",
"@react-native-async-storage/async-storage": "1.18.2",
"@react-native-community/netinfo": "9.3.10",
"@react-native-masked-view/masked-view": "0.2.9",
"@react-navigation/native": "^6.0.11",
"@react-navigation/native-stack": "^6.2.5",
"@sentry/react-native": "5.5.0",
"@tanstack/query-sync-storage-persister": "^4.0.10",
"@tanstack/react-query": "^4.0.10",
"@tanstack/react-query-persist-client": "^4.0.10",
"aws-amplify": "^4.3.22",
"axios": "^0.24.0",
"dayjs": "^1.10.7",
"expo": "^49.0.5",
"expo-app-auth": "~11.1.0",
"expo-application": "~5.3.0",
"expo-auth-session": "~5.0.2",
"expo-blur": "~12.4.1",
"expo-checkbox": "~2.4.0",
"expo-clipboard": "~4.3.0",
"expo-constants": "~14.4.2",
"expo-device": "~5.4.0",
"expo-document-picker": "~11.5.4",
"expo-file-system": "~15.4.2",
"expo-font": "~11.4.0",
"expo-image-manipulator": "~11.3.0",
"expo-image-picker": "~14.3.2",
"expo-linear-gradient": "~12.3.0",
"expo-location": "~16.1.0",
"expo-media-library": "~15.4.1",
"expo-notifications": "~0.18.1",
"expo-random": "~13.2.0",
"expo-screen-orientation": "~6.0.4",
"expo-secure-store": "~12.3.1",
"expo-splash-screen": "~0.20.4",
"expo-status-bar": "~1.6.0",
"expo-updates": "~0.18.11",
"expo-web-browser": "~12.3.2",
"jwt-decode": "^3.1.2",
"moment": "^2.29.1",
"msal": "^1.4.14",
"react": "18.2.0",
"react-aad-msal": "^2.3.5",
"react-dom": "18.2.0",
"react-hook-form": "^7.34.2",
"react-native": "0.72.3",
"react-native-aws3": "0.0.9",
"react-native-calendar-strip": "^2.2.5",
"react-native-calendars": "^1.1269.0",
"react-native-compressor": "^1.1.1",
"react-native-dotenv": "^3.4.9",
"react-native-dropdown-picker": "^5.2.3",
"react-native-geocoding": "^0.5.0",
"react-native-gesture-handler": "~2.12.0",
"react-native-google-places-autocomplete": "^2.4.1",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-maps": "1.7.1",
"react-native-progress": "^5.0.0",
"react-native-radial-gradient": "^1.0.9",
"react-native-reanimated": "~3.3.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.0",
"react-native-select-dropdown": "^2.0.4",
"react-native-svg": "13.9.0",
"react-native-svg-transformer": "^0.14.3",
"react-native-url-polyfill": "^1.3.0",
"react-native-virtualized-view": "^1.0.0",
"react-native-web": "~0.19.6",
"react-native-webview": "13.2.2",
"sentry-expo": "~7.0.0",
"shortid": "^2.2.16",
"use-debounce": "^9.0.3",
"xmldom": "^0.6.0"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"babel-plugin-module-resolver": "^4.1.0"
},
"private": true,
"name": "salesappv2",
"version": "1.0.0",
"resolutions": {
"@expo/config-plugins": "^5.0.0"
}
}
And finally, here is my app.json
{
"expo": {
"name": "Momentum Sales 2.0 (MVP)",
"slug": "MomentumSales",
"scheme": "msauth.com.momentum.salesmvp",
"version": "11.3.4",
"icon": "./assets/icon.png",
"splash": {
"image": "./assets/splash.png",
"resizeMode": "cover",
"backgroundColor": "#ffffff"
},
"updates": {
"fallbackToCacheTimeout": 0,
"url": "https://u.expo.dev"
},
"plugins": [
[
"expo-image-picker",
{
"photosPermission": "The app accesses your photos to let you upload documents.",
"cameraPermission": "This app accesses your camera to let you upload documents."
}
],
[
"expo-screen-orientation",
{
"initialOrientation": "PORTRAIT_UP"
}
],
"sentry-expo"
],
"hooks": {
"postPublish": [
{
"file": "sentry-expo/upload-sourcemaps",
"config": {
"organization": "momentum-solar-bx",
"project": "sales-app"
}
}
]
},
"assetBundlePatterns": ["**/*"],
"ios": {
"supportsTablet": true,
"bundleIdentifier": "com.momentum.salesmvp",
"infoPlist": {
"UISupportedInterfaceOrientations": [
"UIInterfaceOrientationLandscapeRight",
"UIInterfaceOrientationLandscapeLeft",
"UIInterfaceOrientationPortrait"
],
"UISupportedInterfaceOrientations~ipad": [
"UIInterfaceOrientationLandscapeRight",
"UIInterfaceOrientationLandscapeLeft",
"UIDeviceOrientationPortrait",
"UIDeviceOrientationPortraitUpsideDown"
]
}
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#FFFFFF"
},
"permissions": ["android.permission.RECORD_AUDIO"],
"package": "com.momentum.salesmvp"
},
"web": {
"favicon": "./assets/favicon.png"
},
"packagerOpts": {
"config": "metro.config.js",
"sourceExts": ["js", "jsx", "ts", "tsx", "svg"]
},
"runtimeVersion": {
"policy": "sdkVersion"
},
"extra": {
}
}
}
If anyone could point me in the right direction here I would appreciate it so much!
What I have tried:
I have tried deleting my package-lock.json file and reinstalling node modules.
I have tried running expo-doctor --fix
I have tried to run the app through Expo Go on my iPhone instead of the iPad.
I also tried moving the import statement
import * as Notifications from "expo-notifications
directly into the App.js file to verify that it is indeed causing the crash.I tried upgrading expo notifications to version 20, and downgrading to version 18.
Finally I created a brand new expo app and installed the same versions and dependencies. And it works perfectly on my iPhone and iPad through Expo Go. I just don't understand why it's not working on the existing project.