Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using AppLoading in Bare Workflow results in confusing SplashScreen error #7718

Closed
raphaelrk opened this issue Apr 7, 2020 · 12 comments
Closed

Comments

@raphaelrk
Copy link
Contributor

raphaelrk commented Apr 7, 2020

馃悰 Bug Report

Environment

  Expo CLI 3.17.18 environment info:
    System:
      OS: macOS Mojave 10.14.6
      Shell: Unknown - /usr/local/bin/fish
    Binaries:
      Node: 13.11.0 - /usr/local/bin/node
      Yarn: 1.22.4 - /usr/local/bin/yarn
      npm: 6.13.7 - /usr/local/bin/npm
    IDEs:
      Android Studio: 3.3 AI-182.5107.16.33.5199772
      Xcode: 11.3.1/11C504 - /usr/bin/xcodebuild
    npmPackages:
      expo: ~37.0.3 => 37.0.3 
      react: ~16.9.0 => 16.9.0 
      react-native: ~0.61.5 => 0.61.5 
    npmGlobalPackages:
      expo-cli: 3.8.0

iOS Bare Workflow. Targeting simulator. Run with Xcode or yarn ios.

Steps to Reproduce

  • expo init --template bare-minimum
  • cd projectname
  • edit App.js to be like below
  • yarn ios
import * as React from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import { AppLoading } from 'expo';

const instructions = Platform.select({
  ios: `Press Cmd+R to reload,\nCmd+D or shake for dev menu`,
  android: `Double tap R on your keyboard to reload,\nShake or press menu button for dev menu`,
});

const loadAssets = () => new Promise((resolve, reject) => resolve());

export default function App() {

  const [isReady, setIsReady] = React.useState(false);
  if (!isReady) {
    return (
      <AppLoading
        startAsync={loadAssets}
        onFinish={() => setIsReady(true)}
        onError={console.warn}
      />
    );
  }

  return (
    <View style={styles.container}>
      <Text style={styles.welcome}>Welcome to React Native!</Text>
      <Text style={styles.instructions}>To get started, edit App.js</Text>
      <Text style={styles.instructions}>{instructions}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

Expected Behavior

Should get a warning that I'm using AppLoading in a Bare Workflow project when I can't

(Would also be nice if my App.js was mentioned in the redbox, as the flow of error being App.js > AppLoading > SplashScreen. Would also be nice if it was more prominent that AppLoading doesn't work in Bare Workflow, and how instead we should do Asset loading)

Actual Behavior

Get an error about SplashScreen being null (from which point I keep trying to debug splash screen, or think none of my native modules are loading, until realizing removing apploading fixes it)

For googlers:

TypeError: null is not an object (evaluating 'SplashScreen.preventAutoHide')

This error is located at:
    in AppLoading (at AppLoading.js:52)
    in AppLoading (at App.js:17)
    in App
    in RCTView (at AppContainer.js:101)
    in RCTView (at AppContainer.js:119)
    in AppContainer (at renderApplication.js:39)

preventAutoHide
    SplashScreen.js:4:21
AppLoading#constructor
    AppLoadingNativeWrapper.js:6:8
renderRoot
    [native code]:0
runRootCallback
    [native code]:0
forEach
    [native code]:0
Refresh.performReactRefresh
    setUpReactRefresh.js:43:6
setTimeout$argument_0
    require.js:609:10
callFunctionReturnFlushedQueue
    [native code]:0

image

Reproducible Demo

From the steps above: github repo

Related: #4845

Also slightly related: NativeModules empty object

@adamsolomon1986
Copy link

in your node_modules/expo/build/launch/splashScreen.js

change it to the following:

import { NativeModules } from 'react-native';
import* as SplashScreen from 'expo-splash-screen'
export function preventAutoHide() {
    if (SplashScreen.preventAutoHide) {
        SplashScreen.preventAutoHide();
    }
}
export function hide() {
    if (SplashScreen.hide) {
        SplashScreen.hide();
    }
}
//# sourceMappingURL=SplashScreen.js.map

you should be golden

@raphaelrk
Copy link
Contributor Author

raphaelrk commented Apr 7, 2020

@adamsolomon1986 weird, switched to that and put AppLoading back in and that seems to work (my fonts are loading!), but I feel like I shouldn't use that anyway since the docs say AppLoading is only for the managed workflow

Also changed the function names since the ones without "...Async" are deprecated

import { NativeModules } from 'react-native';
// const { ExponentSplashScreen: SplashScreen = {} } = NativeModules;
import * as SplashScreen from 'expo-splash-screen'
export function preventAutoHide() {
    if (SplashScreen.preventAutoHideAsync) {
        SplashScreen.preventAutoHideAsync();
    }
}
export function hide() {
    if (SplashScreen.hideAsync) {
        SplashScreen.hideAsync();
    }
}
//# sourceMappingURL=SplashScreen.js.map

(Also confused about the LaunchScreen.xib still showing on app start before the SplashScreen.storyboard (EDIT! Thought I changed my Launch Screen file to SplashScreen, guess I didn't))

@brettdh
Copy link

brettdh commented Apr 8, 2020

I'm real confused by this situation as well. It seems like a doc bug or an Expo bug or both; I can't tell.

The SDK 37 release notes say that the bare workflow now has feature parity with ExpoKit; that's clearly not the case if AppLoading isn't supported. Could someone please clarify?

@bbarthec
Copy link
Contributor

bbarthec commented Apr 8, 2020

@raphaelrk, @brettdh AppLoading component is indeed removed from bare workflow.
To achieve AppLoading behaviour all you need to do is to follow this examples section in expo-splash-screen README. Using SplashScreen API directly gives you more control over what's happening in your application.

@adamsolomon1986, I highly discourage anyone from modifying files in node_modules directory. Any yarn install or npm install might remove your changes there.

@raphaelrk, regarding LaunchScreen.xib flashing before SplashScreen.storyboard it might be either you are missing native configuration stated here (have you changed Launch Screen?) or this well-known iOS caching problem.

@brettdh
Copy link

brettdh commented Apr 9, 2020

@bbarthec thanks. For anyone stumbling across this, I found the example in the readme to be a bit lacking in terms of replicating the very simple AppLoading behavior that I was relying on:

A React component that tells Expo to keep the app loading screen open if it is the first and only component rendered in your app. Unless autoHideSplash prop is set to false, the loading screen will disappear and your app will be visible when the component is removed.

This simple component replaced it nicely for my usage (with redux-persist PersistGate).

const AppLoading = () => {
  useEffect(() => {
    SplashScreen.preventAutoHideAsync().catch(() => {});
    return () => {
      SplashScreen.hideAsync().catch(() => {});
    };
  });
  return null;
};

// later
...
<PersistGate loading={<AppLoading />} ...>
...
</PersistGate>
...

This may be obvious to others reading this, but since it's so simple, IMO it'd be useful to have as a bridge for folks moving from ExpoKit to bare with SDK 37.

@andrekovac
Copy link
Contributor

This issue is related to #7740 which hits people who newly eject to the bare workflow.

The news that the AppLoading component is not available in the bare worklflow should be clearly stated somewhere when people eject to the bare workflow from the managed one.

andrekovac added a commit to andrekovac/expo that referenced this issue Apr 9, 2020
The issue of not having the `AppLoading` component available in the bare workflow was raised in expo#7718 and expo#7740 . This new example helps people switching from a managed workflow to refactor their `App.tsx` file.

Furthermore, the current examples only include `App.tsx` as class components whereas `App.tsx` in the current Expo templates all use a functional component and hooks. Hence, this example uses a functional component and hooks to look more familiar to new Expo users.
@daybreaker
Copy link

@Andruschenko

It's documented on the Supported Expo SDK APIs page, at the bottom where they show which APIs are bare compatible only, and which are managed only.

@raphaelrk
Copy link
Contributor Author

raphaelrk commented Apr 23, 2020

My eventual solution to showing and hiding the splash screen, though seems a little jank (e.g. maybe I should move hideAsync to a useEffect in App.js):

App.js

import 'expo-asset';
import React, { useEffect, useState } from 'react';
import { Image, StyleSheet } from 'react-native';
import * as SplashScreen from "expo-splash-screen";
import { PersistGate } from 'redux-persist/es/integration/react'
import { Provider } from "react-redux";
import Root from "./Root";
import { store, persistor } from "./redux/store";
import { loadAssets } from "./assets/assets";

// Prevent native splash screen from autohiding before App component declaration
SplashScreen.preventAutoHideAsync()
.then(result => console.log(`SplashScreen.preventAutoHideAsync() succeeded: ${result}`))
.catch(console.warn); // it's good to explicitly catch and inspect any error

const splash = require('./assets/splash/splash.png');

export default function App() {
  const [isReady, setIsReady] = useState(false);
  useEffect(() => {
    loadAssets().then(() => setIsReady(true)).catch(e => {
      console.warn("Error loading assets");
      setIsReady(true);
    });
  }, []);

  const splashJSX = (
    <Image
      source={splash}
      style={StyleSheet.absoluteFill}
      resizeMode={'cover'}
    />
  );

  return (
    <Provider store={store}>
      <PersistGate loading={splashJSX} persistor={persistor}>
        {isReady ? <Root/> : null}
      </PersistGate>
    </Provider>
  );
}

In Root.js --

useEffect(() => {
  console.log("hiding splashscreen");
  SplashScreen.hideAsync();
});

Edit/note: above code possibly runs into this issue in my release build, but I'm not sure (have some confounding bugs), and still getting a really short white flash

@HamzaFunavry
Copy link

in your node_modules/expo/build/launch/splashScreen.js

change it to the following:

import { NativeModules } from 'react-native';
import* as SplashScreen from 'expo-splash-screen'
export function preventAutoHide() {
    if (SplashScreen.preventAutoHide) {
        SplashScreen.preventAutoHide();
    }
}
export function hide() {
    if (SplashScreen.hide) {
        SplashScreen.hide();
    }
}
//# sourceMappingURL=SplashScreen.js.map

you should be golden

Thanks 馃憤

@Jerry-devblog
Copy link

Jerry-devblog commented Aug 11, 2020

there are also the preventAutoHideAsync WARN
SplashScreen.hide() is deprecated in favour of SplashScreen.hideAsync()

modify like so :
`//in your node_modules/expo/build/launch/splashScreen.js

import { NativeModules } from 'react-native';
import* as SplashScreen from 'expo-splash-screen'
export function preventAutoHide() {
if (SplashScreen.preventAutoHideAsync) {
SplashScreen.preventAutoHideAsync();
}
}
export function hide() {
if (SplashScreen.hideAsync) {
SplashScreen.hideAsync();
}
}`

@github-actions
Copy link
Contributor

This issue is stale because it has been open for 60 days with no activity. If there is no activity in the next 7 days, the issue will be closed.

@github-actions github-actions bot added the stale label Feb 17, 2022
@github-actions
Copy link
Contributor

This issue was closed because it has been inactive for 7 days since being marked as stale. Please open a new issue if you believe you are encountering a related problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants