expo / expo

An open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.
https://docs.expo.dev
MIT License
32.99k stars 5.26k forks source link

[SDK43][iOS] Production app is rendered twice, breaking AsyncStorage, RNFirebase, ... #15033

Closed Yonom closed 2 years ago

Yonom commented 2 years ago

Summary

We are experiencing a series of strange issues after upgrading to SDK 43.

They all seem to stem from the fact that the app is mounted twice on launch in production builds, causing all hooks and everything to be run twice.

These issues are severe enough to make our app unusable around 90% of the time (some issues happen consistently, while others come and go). Moreover, the issues stem from race conditions that are super hard to debug, reproduce or work around.

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

bare

What platform(s) does this occur on?

iOS

SDK Version (managed workflow only)

43.0.0

Environment

❯ expo diagnostics                    

  Expo CLI 4.12.11 environment info:
    System:
      OS: macOS 11.6
      Shell: 5.8 - /bin/zsh
    Binaries:
      Node: 16.13.0 - /usr/local/Cellar/node@16/16.13.0/bin/node
      Yarn: 1.22.17 - /usr/local/bin/yarn
      npm: 8.1.0 - /usr/local/Cellar/node@16/16.13.0/bin/npm
      Watchman: 2021.10.18.00 - /usr/local/bin/watchman
    Managers:
      CocoaPods: 1.11.2 - /usr/local/bin/pod
    SDKs:
      iOS SDK:
        Platforms: DriverKit 21.0.1, iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0
      Android SDK:
        API Levels: 23, 24, 28, 29, 30
        Build Tools: 28.0.3, 29.0.2, 30.0.2, 30.0.3, 31.0.0, 31.0.0, 31.0.0, 31.0.0, 31.0.0
        System Images: android-29 | Google APIs Intel x86 Atom, android-29 | Google Play Intel x86 Atom, android-30 | Google APIs Intel x86 Atom, android-30 | Google Play Intel x86 Atom
    IDEs:
      Android Studio: 2020.3 AI-203.7717.56.2031.7784292
      Xcode: 13.1/13A1030d - /usr/bin/xcodebuild
    npmPackages:
      expo: ^43.0.0 => 43.0.1 
      react: 17.0.1 => 17.0.1 
      react-dom: 17.0.1 => 17.0.1 
      react-native: 0.64.2 => 0.64.2 
      react-native-web: 0.17.1 => 0.17.1 
    npmGlobalPackages:
      expo-cli: 4.12.11
    Expo Workflow: bare

Reproducible demo or steps to reproduce from a blank project

Test 1 (baseline, expected behavior)

✅ Output looks alright.

image

Test 2 (upgrade to SDK 43, this is where the issues begin)

🤔 There are two instances of the application being started. There are warnings regarding Unbalanced calls start/end for tag X.

image

Test 3 (add app_open useEffect hook)

image

:x: Our useEffect is run twice, app_open is logged twice. (Expectation: app_open is logged only once)

:x: The first useEffect is not cleaned up before the second one is run; there is no app_close event logged.

Test 4 (react-native-firebase)

(this is an example of a module that breaks because of these changes)

-import React from 'react';
+import React, { useEffect, useState } from 'react';
 import { StyleSheet, Text, View } from 'react-native';
+import firebaseFirestore from '@react-native-firebase/firestore';
+
+const firestore = firebaseFirestore();

 export default function App() {
+  const [success, setSuccess] = useState(false);
+  useEffect(() => {
+    console.log('subscribed');
+    firestore.doc(`test/doc`).onSnapshot(() => {
+      console.log('got snapshot');
+      setSuccess(true);
+    });
+  }, []);
+
   return (
     <View style={styles.container}>
-      <Text>Open up App.js to start working on your app!</Text>
+      {success 
+        ? <Text>Got snapshot</Text>
+        : <Text>Waiting for snapshot</Text>
+      }

❌ Sometimes, when a race condition happens, the first invisible app instance gets the snapshot and the second app is stuck in "Waiting for snapshot" state. Other times, the order is reversed and the second app gets the snapshot, resulting in the expected behavior. (This is somewhat tricky to reproduce, the timings seem to be different based on the device model, app size, jsRuntime, ...)

👎 Bad case: image

👍 Good case: image

Other libraries that have issues

We are experiencing weird race conditions from

Another interesting log we get is EXUpdates: Could not emit noUpdateAvailable event. Did you set the bridge property on the controller singleton?

RodolfoGS commented 2 years ago

Test this PR, maybe is related https://github.com/expo/expo/pull/15019 I fixed an issue with notifications on iOS with that. I created a patch here: https://github.com/expo/expo/issues/14078#issuecomment-959742553

Kudo commented 2 years ago

please try to upgrade expo-updates@0.10.12 with #15019 change and see if it fix the issue for you. thanks for reporting this issue to us.

Yonom commented 2 years ago

@Kudo Yes, I can confirm that hooks are no longer executed twice. Thanks for the fix 👍

Kudo commented 2 years ago

i'm glad to hear that. thank you, @Yonom!