expo / router

[ARCHIVE]: Expo Router has moved to expo/expo -- The File-based router for universal React Native apps
https://docs.expo.dev/routing/introduction/
1.37k stars 114 forks source link

Screen flicker #675

Closed oliviercperrier closed 1 year ago

oliviercperrier commented 1 year ago

Which package manager are you using? (Yarn is recommended)

npm

Summary

I used the authentication/redirect flow proposed in the documentation (https://expo.github.io/router/docs/guides/auth), but it uses a useEffect to make the redirection. All children of the Auth Provider can still be rendered before the redirection is done. That cause page flicker.

Some examples:

  1. Go to Login Page -> Click on Login -> Redirect To Home Page -> (Login Page Flicker) -> Home Page
  2. Trying to access the Login page while already logged in -> Redirect to Home Page -> (Login Page Flicker) -> Home Page

Is there a clean way to prevent page flicker when making these kind of redirection?

Thanks

Minimal reproducible example

Auth Provider

import { useRouter, useSegments } from "expo-router";
import React from "react";

const AuthContext = React.createContext(null);

// This hook can be used to access the user info.
export function useAuth() {
  return React.useContext(AuthContext);
}

// This hook will protect the route access based on user authentication.
function useProtectedRoute(user) {
  const segments = useSegments();
  const router = useRouter();

  React.useEffect(() => {
    const inAuthGroup = segments[0] === "(auth)";

    if (
      // If the user is not signed in and the initial segment is not anything in the auth group.
      !user &&
      !inAuthGroup
    ) {
      // Redirect to the sign-in page.
      router.replace("/sign-in");
    } else if (user && inAuthGroup) {
      // Redirect away from the sign-in page.
      router.replace("/");
    }
  }, [user, segments]);
}

export function Provider(props) {
  const [user, setAuth] = React.useState(null);

  useProtectedRoute(user);

  return (
    <AuthContext.Provider
      value={{
        signIn: () => setAuth({}),
        signOut: () => setAuth(null),
        user,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
}

Root Layout

import { Slot } from "expo-router";
import { Provider } from "../context/auth";

export default function Root() {
  return (
    // Setup the auth context and render our layout inside of it.
    <Provider>
      <Slot />
    </Provider>
  );
}
RichardLindhout commented 1 year ago

Use react-native-mmkv to store authorization state and you will have native+web synchronous access to storage

damianbeles commented 1 year ago

@RichardLindhout the code from the documentation itself is flickering, it doesn't matter if you have or not synchronous access to storage.

RichardLindhout commented 1 year ago

It will matter because in the example it uses asyn storage so it’s not synchronous like mmkv

damianbeles commented 1 year ago

@RichardLindhout maybe you can point out the use (of) async storage from his code. I can't find it. You can achieve the flicker effect without accessing any kind of storage at all, just by using the documented example.

oliviercperrier commented 1 year ago

@RichardLindhout Anyway in a real life example, the auth token or any other credentials will need to be fetched async from a backend api

davidbaselj commented 1 year ago

@oliviercperrier check out https://github.com/expo/router/issues/430, https://github.com/expo/router/issues/559, https://github.com/expo/router/issues/349

I've been wrapping my head around this issue for a few days and it is not working no matter what. Either it is flickering or there is an error The 'navigation' object has not been initialized. And as you said the code copied directly from the documentation is flickering.

I'm waiting for @EvanBacon or @marklawlor response because I'm sure they are aware of this issue but probably busy with releasing router v2. Hopefully this will get fixed 🙂

oliviercperrier commented 1 year ago
RichardLindhout commented 1 year ago

@oliviercperrier What I do in apps is saving the profile in the mmkv for the app to be instant and then refreshing in the background

EvanBacon commented 1 year ago

Appear to be a few different issues here. You can try useLayoutEffect to redirect earlier than useEffect. The navigating before navigation has loaded may require us to move away from React Navigation.

RichardLindhout commented 1 year ago

In react-navigation itself instant navigation is possible by saving the auth state in mmkv and returning the normal stack if user is logged in otherwise return auth stack

oliviercperrier commented 1 year ago

To hide the flicker i added a Load fullscreen on top of everything. I think its a great solution. Loader is much cleaner than a flickering screen.

damianbeles commented 1 year ago

@EvanBacon it seems so. Lots of apps need redirecting before navigation is initialized. If I need to redirect to Y before my X route components activate, I can't do it right now. Maybe see Next.js _middlewares?! Anyway, do you plan to put this issue on the roadmap for v3?

brianwachira commented 1 year ago

Does anyone have a solution to this? I am using AsyncStorage too.

Acetyld commented 1 year ago

Any solution for this? The whole auth/protection feels wonky, navigating away on un-authanticated feels wrong and causing issues

marklawlor commented 1 year ago

@Acetyld We have updated the authentication guide https://docs.expo.dev/router/reference/authentication/, any feedback is welcome.

I'm going to close this issue, as it isn't a bug with the library but a consequence of the architecture in the old guide.

brianwachira commented 1 year ago

@Acetyld We have updated the authentication guide https://docs.expo.dev/router/reference/authentication/, any feedback is welcome.

I'm going to close this issue, as it isn't a bug with the library but a consequence of the architecture in the old guide.

This new authentication guide worked. Thanks!

EvanFarrell commented 6 months ago

in case anyone still has this issue, I found that wrapping a <Redirect /> tag in a view of the intended background color prevented the flicker

<View style={{ flex: 1, backgroundColor: "red" }}>
      <Redirect href={"/home"} />
    </View
thisisomar commented 5 months ago

It seems like when using Redirect as mentioned here in the Auth guide for the AppLayout, there is a slight white flicker.

Is this issue related? Doing what Evan mentioned above does highlight this even more, but the flicker doesn't make sense.

janroures commented 4 weeks ago

@thisisomar yes it still happens. Also followed docs guide and flicker still happens