Upgrading from SDK 43 from 46; Expo Go version works, App and Play store versions crash on load

Expo SDK 46
iOS and Android

Hi, I’ve recently been tasked with updating our app from SDK 43 to 46, primarily because the Google Play Store requires use to target new APIs.

This seemed to go well, although I spent several days resolving dependencies and finding suitable replacements for outdated/abandoned packages. I also had to use patch-package to patch several dependencies that didn’t not have suitable replacements or would require a prohibitive amount of work to upgrade.

The resultant app works well in Expo Go. I’ve also tried starting it locally with npx expo start --no-dev --minify to mimic production conditions. I’m able to use all features of the app and there are a couple known issues with VirtualLists, but other than that, everything works.

We use eas to build and submit and both of these are working as well, with small tweaks to to our Gitlab pipelines to support some of the newer features available (–auto-submit, for instance).

However, once we get the app in stores and try to test out the builds installed from the stores, the app crashes after the splash screen. The error in Android is:

FATAL EXCEPTION: expo-updates-error-recovery
Process: com.asdf.app, PID: 20543
com.facebook.react.common.JavascriptException: TypeError: Cannot read property 'useRef' of null
This error is located at:
    in NavigationContainer
    in MainNavigator
    in UiProvider
    in DataProvider
    in AppProvider
    in RCTView
    in Unknown
    in Root
    in Styled(Root)
    in StyleProvider
    in App
    in ExpoRoot
    in RCTView
    in Unknown
    in RCTView
    in Unknown
    in AppContainer, js engine: hermes, stack:
anonymous@1:1581999
NavigationContainer@1:1577145
renderWithHooks@1:111098
updateForwardRef@1:118844
beginWork$1@1:149508
performUnitOfWork@1:136944
workLoopSync@1:136847
renderRootSync@1:136731
performSyncWorkOnRoot@1:134229
flushSyncCallbacks@1:101389
scheduleUpdateOnFiber@1:131618
updateContainer@1:141971
anonymous@1:151298
renderApplication@1:498422
run@1:492802
runApplication@1:493244
__callFunction@1:164191
anonymous@1:162816
__guard@1:163757
callFunctionReturnFlushedQueue@1:162774
	at com.facebook.react.modules.core.ExceptionsManagerModule.reportException(ExceptionsManagerModule.java:72)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)
	at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:188)
	at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:228)
	at java.lang.Thread.run(Thread.java:920)

Our package.json:

{
    "name": "asdf",
    "version": "46.12.0",
    "description": "asdf",
    "author": "asdf",
    "homepage": "asdf",
    "repository": {
      "type": "git",
      "url": "asdf"
    },
    "main": "node_modules/expo/AppEntry.js",
    "scripts": {
      "start": "npx expo start",
      "build": "npx eas-cli build --platform all",
      "submit": "npx eas-cli submit --latest --platform all",
      "android": "npx expo start --android",
      "ios": "npx expo start --ios",
      "web": "npx expo start --web",
      "eject": "npx expo eject",
      "test": "yarn jest --clearMocks --coverage",
      "test-update": "yarn jest --clearMocks -u",
      "test-watch": "yarn jest --clearMocks --watch",
      "postinstall": "patch-package"
    },
    "dependencies": {
      "@codler/react-native-keyboard-aware-scroll-view": "^2.0.1",
      "@expo/config-plugins": "^5.0.1",
      "@expo/react-native-action-sheet": "^3.13.0",
      "@expo/vector-icons": "^13.0.0",
      "@expo/webpack-config": "^0.17.2",
      "@react-native-async-storage/async-storage": "~1.17.10",
      "@react-native-community/datetimepicker": "^6.2.0",
      "@react-native-community/progress-bar-android": "^1.0.4",
      "@react-native-community/progress-view": "^1.3.2",
      "@react-native-masked-view/masked-view": "^0.2.7",
      "@react-native-picker/picker": "^2.4.4",
      "@react-navigation/bottom-tabs": "^5.11.15",
      "@react-navigation/core": "^5.16.1",
      "@react-navigation/drawer": "^5.12.9",
      "@react-navigation/native": "^5.9.8",
      "@react-navigation/stack": "^5.14.9",
      "@sentry/react-native": "4.2.2",
      "axios": "^0.27.2",
      "babel-plugin-inline-dotenv": "^1.7.0",
      "color": "^3.2.0",
      "dayjs": "^1.11.5",
      "deprecated-react-native-prop-types": "^2.3.0",
      "expo": "^46.0.10",
      "expo-app-loading": "~2.1.0",
      "expo-application": "~4.2.2",
      "expo-constants": "~13.2.3",
      "expo-device": "~4.3.0",
      "expo-firebase-analytics": "~7.1.1",
      "expo-firebase-core": "~5.1.1",
      "expo-font": "~10.2.0",
      "expo-image-picker": "~13.3.1",
      "expo-notifications": "~0.16.1",
      "expo-postpublish-slack-notify": "^1.2.0",
      "expo-status-bar": "~1.4.0",
      "expo-updates": "~0.14.5",
      "lodash": "^4.17.21",
      "metro-minify-terser": "^0.63.0",
      "moment": "^2.29.4",
      "moment-timezone": "^0.5.37",
      "native-base": "^2.15.2",
      "patch-package": "^6.4.7",
      "postinstall-postinstall": "^2.1.0",
      "prop-types": "^15.8.1",
      "react": "^18.2.0",
      "react-dom": "^18.2.0",
      "react-native": "^0.69.5",
      "react-native-communications": "^2.2.1",
      "react-native-gesture-handler": "~2.5.0",
      "react-native-gifted-chat": "^1.0.4",
      "react-native-lightbox": "^0.8.1",
      "react-native-maps": "^0.31.1",
      "react-native-modal": "^11.6.1",
      "react-native-parsed-text": "^0.0.22",
      "react-native-reanimated": "~2.9.1",
      "react-native-safe-area-context": "^4.3.1",
      "react-native-screens": "~3.15.0",
      "react-native-snap-carousel": "^3.9.1",
      "react-native-typing-animation": "^0.1.7",
      "react-native-web": "~0.18.7",
      "react-native-webview": "^11.23.0",
      "sentry-expo": "~5.0.2",
      "use-debounce": "^5.1.0"
    },
    "devDependencies": {
      "@babel/core": "^7.18.6",
      "@babel/plugin-transform-react-jsx-source": "^7.9.0",
      "@babel/preset-env": "^7.18.10",
      "@testing-library/jest-native": "^3.4.3",
      "@testing-library/react-native": "^7.1.0",
      "babel-eslint": "^10.1.0",
      "babel-jest": "^26.6.2",
      "babel-plugin-module-resolver": "^4.0.0",
      "babel-preset-expo": "~9.2.0",
      "eslint": "^6.8.0",
      "eslint-config-airbnb": "^18.2.0",
      "eslint-plugin-flowtype": "^4.7.0",
      "eslint-plugin-import": "^2.22.1",
      "eslint-plugin-jsx-a11y": "^6.4.1",
      "eslint-plugin-prettier": "^3.1.2",
      "eslint-plugin-react": "^7.21.5",
      "eslint-plugin-react-hooks": "^4.2.0",
      "eslint-plugin-react-native": "^3.8.1",
      "flow-bin": "^0.186.0",
      "flow-typed": "^3.8.0",
      "husky": "^4.2.3",
      "jest": "^27.5.1",
      "jest-cli": "^27.5.1",
      "jest-expo": "^46.0.1",
      "jest-html-reporter": "^3.6.0",
      "jest-junit": "^14.0.1",
      "jest-junit-reporter": "^1.1.0",
      "jest-mock-axios": "^4.6.1",
      "pre-commit": "^1.2.2",
      "prettier": "^2.7.1",
      "react-test-renderer": "^18.2.0",
      "standard-version": "^9.5.0",
      "standard-version-expo": "^1.0.3"
    },
    "resolutions": {
      "@codler/react-native-keyboard-aware-scroll-view": "2.0.1",
      "@expo/config-plugins": "5.0.1",
      "@react-native-community/datetimepicker": "6.2.0",
      "@react-native-picker/picker": "2.4.4",
      "react": "18.2.0"
    },
    "private": true
  }

It appears as if the error is occurring in our MainNavigator:

import React, { useRef } from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import * as Analytics from 'expo-firebase-analytics';
import SignUp from '_screens/Auth/SignUp';
import Splash from '_screens/Auth/Splash';
import Login from '_screens/Auth/Login';
import ForgotPassword from '_screens/Auth/ForgotPassword';
import DrawerNavigator from '_navigators/DrawerNavigator';
import EventList from '_screens/Event/EventList';
import InAppBrowserScene from '_screens/InAppBrowser';

const MainStack = createStackNavigator();
/**
 * Main App Navigator
 * Contains:
 * Auth screens
 * Event List screen
 * Main Drawer screen (Home)
 * InAppBrowser screen
 *
 * @returns {JSX.Element}
 * @constructor
 */

const MainNavigator = () => {
  const routeNameRef = useRef();
  const navigationRef = useRef();

  return (
    <NavigationContainer
      ref={navigationRef}
      onReady={() => {
        routeNameRef.current = navigationRef.current.getCurrentRoute().name;
      }}
      onStateChange={async () => {
        const previousRouteName = routeNameRef.current;
        const currentRouteName = navigationRef.current.getCurrentRoute().name;

        if (previousRouteName !== currentRouteName) {
          await Analytics.logEvent('screen_view', {
            firebase_screen: currentRouteName,
            firebase_screen_class: currentRouteName
          });
        }
        routeNameRef.current = currentRouteName;
      }}
    >
      <MainStack.Navigator initialRouteName="Splash" headerMode="none">
        <MainStack.Screen name="Splash" component={Splash} />
        <MainStack.Screen name="EventList" component={EventList} options={{ unmountOnBlur: true }} />
        <MainStack.Screen name="Login" component={Login} options={{ unmountOnBlur: true }} />
        <MainStack.Screen name="Drawer" component={DrawerNavigator} options={{ unmountOnBlur: true }} />
        <MainStack.Screen name="SignUp" component={SignUp} />
        <MainStack.Screen name="ForgotPassword" component={ForgotPassword} />
        <MainStack.Screen name="InAppBrowser" component={InAppBrowserScene} options={{ unmountOnBlur: true }} />
      </MainStack.Navigator>
    </NavigationContainer>
  );
}

export default MainNavigator;

But even removing the code that calls useRef() (and the associated analytics code), the error persists.

Our App.js, in case it is useful:

import React from 'react';
import 'react-native-gesture-handler';
import { Root, StyleProvider } from 'native-base';

import getTheme from '_theme/components';
import variables from '_theme/variables/commonColor';
import MainNavigator from '_navigators/MainNavigator';
import { AppProvider } from '_contexts/AppContext';
import { DataProvider } from '_contexts/DataContext';
import { UiProvider } from '_contexts/UiContext';
import PushNotifications from '_components/PushNotifications';

import * as Sentry from 'sentry-expo';

Sentry.init({
  dsn: process.env.SENTRY_DSN,
  enableInExpoDevelopment: true,
  debug: true
});

const App = () => {
  return (
    <StyleProvider style={getTheme(variables)}>
      <Root>
        <AppProvider>
          <DataProvider>
            <UiProvider>
              <PushNotifications />
              <MainNavigator />
            </UiProvider>
          </DataProvider>
        </AppProvider>
      </Root>
    </StyleProvider>
  );
};

export default App;

Debugging this has been time consuming- since Expo Go works fine, we have to build and submit and then either wait for the Play Store’s pre launch testing, or install locally and wait for the errors to start showing up in Sentry. Resolving the issue would be great, but at the very least, reproducing locally and being able to get better log information (non-bundled location and line numbers) could at least point us in the direction.

Does anyone have any ideas? I’ve maintained this project for a couple years and through a couple Expo SDK upgrades, but I’m reaching the end of my domain knowledge here. Thanks!

Hi @cgitech

Maybe building a version with jsc instead of hermes as a test will give you something.

Also, have you tried building a dev or preview version and installing it locally to see if you get any useful info in device logs etc?

Hi @wodin, thanks for the suggestion. Using jsc instead of hermes produced slightly different errors, but those had enough information for me to track down issues and resolve the majority of them.

1 Like