Search code examples
javascriptreactjsreact-nativesplash-screenuse-effect

Why isn't react-native-onboarding-swiper working on my splash screen?


I have an application created doing my React Native studies. Once finished I have added some presentation screens and added 'react-native-onboarding-swiper' which seemed to me the easiest way that I found. I have also added '@react-native-async-storage/async-storage', before adding the splash screens, the App worked without AsyncStorage

The idea is that the App shows the presentation screens when we install it and open it for the first time, and then, it opens in the menu screen that I show.

EDIT AGAIN:

I need to correct the condition so that splash screens show only the first time the app is started, then it should boot to the home screen. 2 - The home screen is the one that I show in the screenshot and it shows me the button to "Go Back". I need this to go away

I cannot use headerShown: false, since in other screens that the application has if I must have the "Go Back" button How can I correct these problems in the simplest way?

Image enter image description here

The application works perfectly with the code in App.js that I show below, but it shows the presentation screens whenever the App is opened. I have created a condition to correct this, since I need the Presentation screens, to be shown, increase the first time the Application is opened. This is what App.js looks like before adding the else if:

import "react-native-gesture-handler";
import React, { useEffect } from "react";

import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import AsyncStorage from "@react-native-async-storage/async-storage";
import SplashScreen from "react-native-splash-screen";

import NuevaOrden from "./views/NuevaOrden";
import OnboardingScreen from "./views/OnboardingScreen";

const Stack = createStackNavigator();

const App = () => {

//Hide Splash screen on app load.
  React.useEffect(() => {
    SplashScreen.hide();
  })  return (
    <>
      <FirebaseState>
        <PedidoState>
          <NavigationContainer>
            <Stack.Navigator
              screenOptions={{
                headerStyle: {
                  backgroundColor: "#FFDA00",
                },
                headerTitleStyle: {
                  fontWeight: "bold",
                },
                headerTintColor: "#000",
              }}
            >
              <Stack.Screen
                name="OnboardingScreen"
                component={OnboardingScreen}
                options={{
                  title: "Restaurante Paky",
                }}
              />

              <Stack.Screen
                name="NuevaOrden"
                component={NuevaOrden}
                options={{
                  title: "Restaurante Paky"
                }}
              />

              <Stack.Screen
                name="Menu"
                component={Menu}
                options={{
                  title: "Menú",
                  headerRight: props => <BotonResumen />
                }}
              />

              <Stack.Screen
                name="DetallePlato"
                component={DetallePlato}
                options={{
                  title: null
                }}
              />

              <Stack.Screen
                name="FormularioPlato"
                component={FormularioPlato}
                options={{
                  title: "Mi Pedido"
                }}
              />

              <Stack.Screen
                name="ResumenPedido"
                component={ResumenPedido}
                options={{
                  title: "Resumen Pedido"
                }}
              />

              <Stack.Screen
                name="ProgresoPedido"
                component={ProgresoPedido}
                options={{
                  title: "Progreso de Pedido"
                }}
              />
            </Stack.Navigator>
          </NavigationContainer>
        </PedidoState>
      </FirebaseState>
    </>
  );
};

export default App;

Everything works perfectly, but when I have placed the application inside an else if, it has broken showing the following error:

ExceptionsManager.js: 180

TypeError: Invalid attempt to destructure non-iterable instance.
In order to be iterable, non-array objects must have a [Symbol.iterator] () method.
This error is located at:
    in App (at renderApplication.js: 48)
    in RCTView (at View.js: 32)
    in View (at AppContainer.js: 106)
    in RCTView (at View.js: 32)
    in View (at AppContainer.js: 133)
    in AppContainer (at renderApplication.js: 41)
    in restaurantapp (RootComponent) (at renderApplication.js: 57)

I have looked for solutions without success, I need the splash screens to show only when we use the app for the first time, and then the App starts from the main menu This are my questions:

1 - Can I get this somehow? 2 - Can I add the presentation screens without using AsyncStorage?

I show the files involved:

File App.js

import "react-native-gesture-handler";
import React, { useEffect } from "react";

import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import AsyncStorage from "@react-native-async-storage/async-storage";

import OnboardingScreen from "./views/OnboardingScreen";

const Stack = createStackNavigator();

const App = () => {
  const [isFirstLaunch, setIsFirstLaunch] = React.useEffect(null);

  useEffect(() => {
    AsyncStorage.getItem("alreadyLaunched").then((value) => {
      if (value == null) {
        AsyncStorage.setItem("alreadyLaunched", "true");
        setIsFirstLaunch(true);
      } else {
        setIsFirstLaunch(false);
      }
    });
  }, []);

  if (isFirstLaunch === null) {
    return null;
  } else if (isFirstLaunch === true) {
    return (
      <>
        <FirebaseState>
          <PedidoState>
            <NavigationContainer>
              <Stack.Navigator
                screenOptions={{
                  headerStyle: {
                    backgroundColor: "#FFDA00",
                  },
                  headerTitleStyle: {
                    fontWeight: "bold",
                  },
                  headerTintColor: "#000",
                }}
              >
                <Stack.Screen
                  name="OnboardingScreen"
                  component={OnboardingScreen}
                  options={{
                    title: "Restaurante Paky",
                  }}
                />

                <Stack.Screen
                name="NuevaOrden"
                component={NuevaOrden}
                options={{
                  title: "Restaurante Paky"
                }}
              />

              <Stack.Screen
                name="Menu"
                component={Menu}
                options={{
                  title: "Menú",
                  headerRight: props => <BotonResumen />
                }}
              />

              <Stack.Screen
                name="DetallePlato"
                component={DetallePlato}
                options={{
                  title: null
                }}
              />

              <Stack.Screen
                name="FormularioPlato"
                component={FormularioPlato}
                options={{
                  title: "Mi Pedido"
                }}
              />

              <Stack.Screen
                name="ResumenPedido"
                component={ResumenPedido}
                options={{
                  title: "Resumen Pedido"
                }}
              />

              <Stack.Screen
                name="ProgresoPedido"
                component={ProgresoPedido}
                options={{
                  title: "Progreso de Pedido"
                }}
              />
              </Stack.Navigator>
            </NavigationContainer>
          </PedidoState>
        </FirebaseState>
      </>
    );
  } else {
    return <NuevaOrden />;
  }
};

export default App;

File OnboardingScreen.js

import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator, useNavigation } from "@react-navigation/stack";
import {
  View,
  Text,
  Button,
  Image,
  StyleSheet,
  TouchableOpacity,
} from "react-native";
import Onboarding from "react-native-onboarding-swiper";

const Dots = ({ selected }) => {
  let backgroundColor;

  backgroundColor = selected ? "rgba(0, 0, 0, 0.8)" : "rgba(0, 0, 0, 0.3)";

  return (
    <View
      style={{
        width: 6,
        height: 6,
        marginHorizontal: 3,
        backgroundColor,
      }}
    />
  );
};



const Next = ({ ...props }) => (
  <Button title="Next" color="#000000" {...props} />
);

const Done = ({ ...props }) => (
  <TouchableOpacity style={{ marginHorizontal: 8 }} {...props}>
    <Text style={{ fontSize: 16 }}>Done</Text>
  </TouchableOpacity>
);
const OnboardingScreen = ({ navigation }) => {
  return (
    <Onboarding
      SkipButtonComponent={Skip}      
      DoneButtonComponent={Done}
      DotComponent={Dots}
      onSkip={() => navigation.navigate("NuevaOrden")}       
      onDone={() => navigation.navigate("NuevaOrden")}
      pages={[
        {
          backgroundColor: "#a6e4d0",
          image: <Image source={require("../assets/onboarding-img1.png")} />,
          title: "Onboarding 1",
          subtitle: "Done with React Native Onboarding Swiper",
        },
        {
          backgroundColor: "#fdeb93",
          image: <Image source={require("../assets/onboarding-img2.png")} />,
          title: "Onboarding 2",
          subtitle: "Done with React Native Onboarding Swiper",
        },
        {
          backgroundColor: "#e9bcbe",
          image: <Image source={require("../assets/onboarding-img3.png")} />,
          title: "Onboarding 3",
          subtitle: "Done with React Native Onboarding Swiper",
        },
      ]}
    />
  );
};

export default OnboardingScreen;


Solution

  • It seems you need to render the NuevaOrden component inside the NavigationContainer component, try conditionally rendering the onboarding screens with the and operator && and in your onboarding component call navigation.replace instead of navigation.navigate to set the NuevoPedido screen as the first screen and avoid the back button.

    import "react-native-gesture-handler";
    import React, { useEffect } from "react";
    
    import { NavigationContainer } from "@react-navigation/native";
    import { createStackNavigator } from "@react-navigation/stack";
    import AsyncStorage from "@react-native-async-storage/async-storage";
    
    import OnboardingScreen from "./views/OnboardingScreen";
    
    const Stack = createStackNavigator();
    
    const App = () => {
      const [isFirstLaunch, setIsFirstLaunch] = React.useState(null);
    
      useEffect(() => {
        AsyncStorage.getItem("alreadyLaunched").then((value) => {
          if (value == null) {
            AsyncStorage.setItem("alreadyLaunched", "true");
            setIsFirstLaunch(true);
          } else {
            setIsFirstLaunch(false);
          }
        });
      }, []);
    
      if (isFirstLaunch === null) {
        return null;
      } else {
        return (
          <>
            <FirebaseState>
              <PedidoState>
                <NavigationContainer>
                  <Stack.Navigator
                    screenOptions={{
                      headerStyle: {
                        backgroundColor: "#FFDA00",
                      },
                      headerTitleStyle: {
                        fontWeight: "bold",
                      },
                      headerTintColor: "#000",
                    }}
                  >
                    {isFirstLaunch && (
                      <Stack.Screen
                        name="OnboardingScreen"
                        component={OnboardingScreen}
                        options={{
                          title: "Restaurante Paky",
                        }}
                      />
                    )}
                    <Stack.Screen
                      name="Nueva Orden"
                      component={NuevaOrden}
                      options={{
                        title: "Nueva orden",
                      }}
                    />
                    <Stack.Screen
                      name="Menu"
                      component={Menu}
                      options={{
                        title: "Restaurante Paky",
                      }}
                    />
                    ... here the rest of your screens
                  </Stack.Navigator>
                </NavigationContainer>
              </PedidoState>
            </FirebaseState>
          </>
        );
      }
    };
    
    export default App;
    

    in your Onboarding component

          onSkip={() => navigation.replace("NuevaOrden")}       
          onDone={() => navigation.replace("NuevaOrden")}