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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

White screen on transition while using "Protected routes" #10894

Open
3 of 12 tasks
leobHumi opened this issue Sep 30, 2022 · 9 comments
Open
3 of 12 tasks

White screen on transition while using "Protected routes" #10894

leobHumi opened this issue Sep 30, 2022 · 9 comments

Comments

@leobHumi
Copy link

Current behavior

I have an authentication flow pretty similar to the one described in the documentation. A loading screen, an authentication flow and an authenticated flow.

While using RN 0.67, @react-navigation/native 6.0.8, @react-navigation/native-stack 6.5.2, the implementation was working properly, with the screens being transitioned as needed, as it would be expected.

But now I started to migrate my project to the latest version of RN, 0.70.1, enabling both Hermes and Fabric, and the same flow doesn't work anymore. The first screen will show as expected, but when the flag changes and the navigation is supposed to transition to the new screen, the app just gets blank, no visible error or crash.

Navigation is working properly. If I don't use the Protected routes pattern, I can normally navigate back and forth between the screens of my flow, but using the Pattern doesn't work.

The following code is pretty similar to what I have in my real app, and it behaves the same way, with a white screen. (The provided repo is basically this code with all the configuration I have in a testing project)

import React, {useEffect, useState} from 'react';
import {View} from 'react-native';

import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';

const Stack = createNativeStackNavigator();

const App = () => {
  const [isRed, setIsRed] = useState(false);

  useEffect(() => {
    const x = setInterval(() => setIsRed(!isRed), 1000);

    return () => {
      clearInterval(x);
    };
  }, [isRed]);

  console.log('isRed: ', isRed);

  return (
    <NavigationContainer>
      <Stack.Navigator>
        {isRed ? (
          <Stack.Screen name="Red" component={Red} />
        ) : (
          <Stack.Screen name="Blue" component={Blue} />
        )}
      </Stack.Navigator>
    </NavigationContainer>
  );
};

const Red = () => <View style={{flex: 1, backgroundColor: 'red'}} />;

const Blue = () => <View style={{flex: 1, backgroundColor: 'blue'}} />;

export default App;

The "Blue" screen will show as expected, and when 1s passes, the screen gets blank and "Blue" is still the name of the navigation, but the "Red" screen never shows, as you can see in the video:

Simulator.Screen.Recording.-.iPhone.14.-.2022-09-29.at.19.10.35.mp4

Worth mentioning that I'm using a M1 Macbook, since RN had issues with it in the past, but I think it's not related.

Expected behavior

While using the Protected routes pattern, when the controlling flag changes, the navigation stack should transition between the screens provided, instead of showing a white screen.

Reproduction

https://github.com/leobHumi/ReactNavigationBug

Platform

  • Android
  • iOS
  • Web
  • Windows
  • MacOS

Packages

  • @react-navigation/​bottom-tabs
  • @react-navigation/​drawer
  • @react-navigation/​material-bottom-tabs
  • @react-navigation/​material-top-tabs
  • @react-navigation/​stack
  • @react-navigation/​native-stack

Environment

  • I've removed the packages that I don't use
package version
@react-navigation/native 6.0.13
@react-navigation/native-stack 6.9.0
react-native-safe-area-context 4.4.1
react-native-screens 3.17.0
react-native 0.70.1
node 16
yarn 1.22.15
@leobHumi
Copy link
Author

Any updates on this?

@tegozen
Copy link

tegozen commented Oct 18, 2022

image

I don’t know how to solve such a problem correctly, but I just set the view to be like a transparent modal. Then the background of the navigator shines through on the background of the application, and there you can already choose a color for the theme.

@tegozen
Copy link

tegozen commented Oct 18, 2022

Any updates on this?

mini solution)

@leobHumi
Copy link
Author

@tegozen thanks for the suggestion. I tested it here and it doesn't solve the initial issue.

And, in using the presentation mode of a Modal, it would change the animations of my navigation flow, deviating from the original animations users are used for iOS for normal screen transitions.

@cospin
Copy link

cospin commented Jan 4, 2023

I'm having the same issue with the native stack and RN v0.70.6, has anyone found a solution?
Actions like replace or reset don't work either.

@cospin
Copy link

cospin commented Jan 7, 2023

btw. here the issue in the react-native-screens repo: software-mansion/react-native-screens#1628

So, in the meanwhile a workaround is to use plain React Native Views by disabling react-native-screens: https://github.com/software-mansion/react-native-screens#disabling-react-native-screens

@skam22
Copy link

skam22 commented Sep 13, 2023

just ran into this issue on Android 10 devices using a NativeStackNavigator.

I was able to work around the blank screen issue by moving the conditional rendering outside of the render and setting a key on the navigator to force a refresh.

previously:

const App = () => {
  const {currentUser} = useAuthListener();  // custom hook that listens for changes to auth.

  return (
    <View style={styles.container}>
      <RootStack.Navigator>
        {currentUser ? (
          <RootStack.Screen
            name={RootStackRoutes.Home}
            component={HomeScreen}
          />
          ) : (
          <RootStack.Screen
            name={RootStackRoutes.Auth}
            component={AuthScreen}
          />
          )
        }
      </RootStack.Navigator>
    </View>
  );
};

working code:

const App = () => {
  const {currentUser} = useAuthListener();  // custom hook that listens for changes to auth.

  if (currentUser) {
    return (
      <View style={styles.container}>
        <RootStack.Navigator key="has_current_user">
          <RootStack.Screen
            name={RootStackRoutes.Home}
            component={HomeScreen}
          />
        </RootStack.Navigator>
      </View>
    );
  };

  return (
    <View style={styles.container}>
      <RootStack.Navigator key="no_current_user">
        <RootStack.Screen
          name={RootStackRoutes.Auth}
          component={AuthScreen}
        />
      </RootStack.Navigator>
    </View>
  );
};

@DorianMazur
Copy link

DorianMazur commented Oct 11, 2023

@skam22
This will also work:

const App = () => {
  const {currentUser} = useAuthListener();  // custom hook that listens for changes to auth.

  return (
    <View style={styles.container}>
      <RootStack.Navigator key={currentUser ? "has_current_user" : "no_current_user"}>
       {currentUser ? (
          <RootStack.Screen
            name={RootStackRoutes.Home}
            component={HomeScreen}
          />
          ) : (
          <RootStack.Screen
            name={RootStackRoutes.Auth}
            component={AuthScreen}
          />
          )
        }
      </RootStack.Navigator>
    </View>
  );
};

@JDMathew
Copy link

I fixed this by delaying my async hook by a few mili seconds with await new Promise((res) => setTimeout(res, 500));

Here is an example:

export const AppNavigation = () => {
  const { authDispatch } = useAuthContext();

  React.useEffect(() => {
    const bootstrapAsync = async () => {
      try {
        await new Promise((res) => setTimeout(res, 500)); // allows react navigation to render the screen for transition
        const user = await someAuthFunction();
        authDispatch(
          authActions.restoreToken(user),
        );
      } catch (e) {
        // Failed to get token ensure user is signed out
        authDispatch(authActions.signOut());
      }
    };
    bootstrapAsync();
  }, [authDispatch]);

  return (
    <NavigationContainer fallback={<LoadingScreen />} />
      <RootStackNavigator />
    </NavigationContainer>
  );
};


const App = () => {
  const { authState } = useAuthContext(); // custom hook that listens for changes to auth.
  const { isLoggedIn } = authState;

  return (
    <AppNavigation >
      <RootStack.Navigator>
       {isLoggedIn ? (
          <RootStack.Screen
            name={RootStackRoutes.Home}
            component={HomeScreen}
          />
          ) : (
          <RootStack.Screen
            name={RootStackRoutes.Auth}
            component={AuthScreen}
          />
          )
        }
      </RootStack.Navigator>
    </AppNavigation>
  );
};

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

6 participants