8

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;

Miguel Espeso
  • 194
  • 1
  • 7
  • 26
  • 3
    Probably the error is in line: `const [isFirstLaunch, setIsFirstLaunch] = React.useEffect(null)`. Should be `useState` – Samuli Hakoniemi Sep 06 '21 at 11:36
  • Thanks @SamuliHakoniemi. That is correct since if I do not put the code inside the `else if`, everything works, the screens are passed. But when I have created the condition to only show SplashScreen the first time we see the App, it stops working – Miguel Espeso Sep 06 '21 at 11:42
  • 2
    What do you mean by that? Do you get the same issue when you change to: `const [isFirstLaunch, setIsFirstLaunch] = React.useState(null)` ? – Sinan Yaman Sep 08 '21 at 13:13
  • When I switch to UseState I get an error on the line: `else {return }`. ERROR: **Error: Couldn't find a navigation object. Is your component inside NavigationContainer?** – Miguel Espeso Sep 08 '21 at 13:28

1 Answers1

3

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")}
diedu
  • 19,277
  • 4
  • 32
  • 49
  • Thanks @diedu . Sorry to say your idea doesn't work: `ERROR Error: A navigator can only contain 'Screen', 'Group' or 'React.Fragment' as its direct children (found 'OnboardingContainer'). To render this component in the navigator, pass it in the 'component' prop to 'Screen'.`.This error is located at: in StackNavigator (at App.js – Miguel Espeso Sep 12 '21 at 18:34
  • 1
    @MiguelEspeso well, that's sad. I updated the answer with an alternative using a ternary operator instead to render the onboarding or your other screens – diedu Sep 12 '21 at 20:55
  • Thanks again @diedu . I have tried and the App freezes on the loading screen. I get the following error again: `ERROR 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)` – Miguel Espeso Sep 13 '21 at 05:05
  • 1
    @MiguelEspeso sorry I copied the wrong code, I left the useEffect hook. I changed to useState, please try again – diedu Sep 13 '21 at 08:21
  • Can @diedu help me to accept your answer as good? – Miguel Espeso Sep 15 '21 at 16:29
  • 1
    @MiguelEspeso are you still having issues? could you update the question with the new errors you're getting? – diedu Sep 15 '21 at 16:32
  • I already updated the question. I have added all the screens that my App has since the first time I only put two screens. Having more, the error is that it does not find the rest of the screens when I click on the buttons to navigate between screens. Now there are the screens that are. The first screen accessed after passing the `OnboardingScreen` tutorial must be `NewOrder`. Can you help me @diedu ? – Miguel Espeso Sep 15 '21 at 17:14
  • 1
    @MiguelEspeso I updated my answer. I think it didn't find the other screens because we were rendering them only when `isFirstLaunch` was false. With the update, it should render the rest of the screens regardless. I'm not sure if the flow should change after the first launch. Would the NuevaOrden component always be the first screen or only when the onboarding is shown? – diedu Sep 16 '21 at 03:54
  • If @diedu , NewOrder is always the first screen. This leads us to Menu, it is where the products in the catalog are displayed. Then this: PlateDetail, PlateForm, Order Summary, Order Process When I install the App for the first time, it works correctly, I can enter all the screens, but when closing and opening again, when I try to enter the Menu, I receive this error: `The action 'NAVIGATE' with payload {"name":"Menu"} was not handled by any navigator.Do you have a screen named 'Menu'?If you're trying to navigate to a screen in a nested navigator` – Miguel Espeso Sep 16 '21 at 04:33
  • 1
    @MiguelEspeso do you have some time to enter a chat? https://chat.stackoverflow.com/rooms/237159/try-to-fix-navigation-issue – diedu Sep 16 '21 at 04:41