Native splash screen is already hidden. Call this method before rendering any view.

Please provide the following:

  1. SDK Version: 39.0.2
  2. Platforms(Android/iOS/web/all): iOS
  3. Add the appropriate “Tag” based on what Expo library you have a question on.

Hi everybody, i looking for some help with expo-splash-screen

See, i’m getting this error and a white screen when testing my app in expo iOS client (Production mode) and when test my standalone app on iOS Test Flight but not in android
[Error: Native splash screen is already hidden. Call this method before rendering any view.]

so, basically, i prevent the splash from hiding in my app.js this is the code

import React, {
  useState,
  useReducer,
  useCallback,
  useMemo,
  useEffect,
} from "react";
import { View, StatusBar, SafeAreaView, StyleSheet } from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import * as SplashScreen from "expo-splash-screen";
import AsyncStorage from "@react-native-community/async-storage";
import {
  AuthContext,
  SettingContext,
} from "./src/contexts/index";
import { authInitialState, authReducer } from "./src/reducers/auth";
import { settingReducer } from "./src/reducers/setting";

const Stack = createStackNavigator();

export default function App() {
  const [isAppReady, setIsAppReady] = useState(false);
  const [splashScreenClosed, setSplashScreenClosed] = useState(false);

  const [authState, authDispatch] = useReducer(authReducer, authInitialState);
  const [settingState, settingDispatch] = useReducer(settingReducer, {});

const authContext = useMemo(
    () => ({
      signIn: () => authDispatch({ type: "SIGN_IN" }),
      signOut: () => authDispatch({ type: "SIGN_OUT" }),
      ),
    [authState.user]
  );

  //Settings
  const settingContext = useMemo(
    () => ({ 
       setSetting: (setting) => settingDispatch({ type: "SET", setting }),
      setting: settingState,
    }),
    [settingState]
  );

useEffect(() => {
    const fetchCommonData = async () => {
      firestore
        .collection("mycollection")
        .doc("client")
        .onSnapshot(
          (doc) => {
            settingContext.setSetting(doc.data());
          },
          (error) => console.error(error)
        );

      const providersResult = await firestore.collection("providers").get();
      const providers = providersResult.docs.map((doc) => ({
        id: doc.id,
        ...doc.data(),
      }));

      providersContext.addProviders(providers);
    };
    //AsyncStorage.clear();
    const initApp = async () => {
      await SplashScreen.preventAutoHideAsync();
      await fetchCommonData();
      const storage = await AsyncStorage.getItem(KEY);
    };

    initApp()
      .then(() => {
        firebase.auth().onAuthStateChanged(
          async (user) => {
            if (user) {
              
              firestore
                .collection("mycollection")
                .doc(user.uid)
                .onSnapshot(
                  async (doc) => {
                    const userInfo = { id: user.uid, ...doc.data() };
                    authContext.updateLoggedInUser(userInfo);
                    authContext.signIn();
                    setIsAppReady(true);
                  },
                  () => {}
                );
            } else {
              authContext.signOut();
              cleanAllDataState();
              setIsAppReady(true);
            }
          },
          () => {}
        );
      })
      .catch((error) => {
        console.error(error);
      });

  }, []);

const onLayoutRootView = useCallback(async () => {
    if (isAppReady && !splashScreenClosed) {
      setSplashScreenClosed(true);
      await SplashScreen.hideAsync();
    }
  }, [isAppReady, splashScreenClosed]);

  if (!isAppReady) return null;

return (
    <View style={{ ...StyleSheet.absoluteFill }} onLayout={onLayoutRootView}>
      <AuthContext.Provider value={authContext}>
        <SafeAreaView />
        <StatusBar
        />
        <SettingContext.Provider value={settingContext}>
                    <NavigationContainer
                    >
                      <Stack.Navigator
                        screenOptions={{
                          headerShown: false,
                        }}
                      >
  
                        <Stack.Screen name="Main" component={MainTab} />
          
                      </Stack.Navigator>
                    </NavigationContainer>
        </SettingContext.Provider>
      </AuthContext.Provider>
    </View>
  );

}

my app prevent the splash from closing before loading some data and then closes de splash screen but i get the error message. is there something i’m missing?

this can happen if SplashScreen.hideAsync() is called during fast refresh, because the splash screen has already been hidden

Thanks for replying! I’m aware of it but this happen in standalone app as well (Just shows a white screen)

i recently updated the docs to explain the ‘white screen’ issue that you’re encountering:

SplashScreen.hideAsync()
Hides the native splash screen immediately. Be careful to ensure that your app has content ready to display when you hide the splash screen, or you may see a blank screen briefly. See the “Usage” section for an example.

Yeah, i read the documentation and followed the instructions that’s why i’m using this

const onLayoutRootView = useCallback(async () => {
    if (isAppReady && !splashScreenClosed) {
      setSplashScreenClosed(true);
      await SplashScreen.hideAsync();
    }
  }, [isAppReady, splashScreenClosed]);

and this

<View style={{ ...StyleSheet.absoluteFill }} onLayout={onLayoutRootView}>

but i’m still facing the issue… and as i mentioned in the content of the question, this only happens in iOS

react navigation hasn’t necessarily rendered all of its contents by the time the root view has rendered. just because the container and navigator are mounted does not guarantee that it has drawn pixels to the screen - it may have mounted the component but be in a rendering pass where it is currently measuring the layout or performing other types of async work. unfortunately this is a bit complicated to handle nicely, we definitely need better apis around this.

Got you! but, can you explain to me why the app works just fine when it’s in development mode in iOS client and works perfectly in android?

i cannot explain that, it would require more knowledge about the code in your app. if you investigate that further, i’d be curious what you find

Hi @notbrent, do you think that calling SplashScreen.preventAutoHideAsync(); outside App.js main function could cause any issue?
ig.

SplashScreen.preventAutoHideAsync();
export default function App() {
//rest of the code
}

instead of inside an useEffect

useEffect(() => {
    async function prepare() {
      try {
        await SplashScreen.preventAutoHideAsync();
      } catch (e) {
        console.warn(e);
      } finally {
        setAppIsReady(true);
      }
    }

    prepare();
  }, []);

I don’t know why but doing that made my app work again. (have only tested it in Expo Go Dev/Production mode not in standalone app yet)