最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to persist auth across app refresh in React Native app - Stack Overflow

programmeradmin2浏览0评论

I am using React Native / Expo to build an app with auth. I can log in fine but when I refresh the app, the auth is lost and I need to log in again.

My firebase config looks like this:

import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  apiKey: "<key>",
  authDomain: "<name>.firebaseapp",
  projectId: "<id>",
  storageBucket: "<id>.appspot",
  messagingSenderId: "<sender_id>",
  appId: "<app_id>",
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

export { auth };

I have a self hosted log in form in my app that is as so

import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';

const LoginScreen = () => {
  const auth = getAuth();
  
  const handleLogin = useCallback(async (email: string, password: string) => {
    const user = await signInWithEmailAndPassword(auth, email, password);
  }, [auth]);

  return (
    <LoginForm onLogin={handleLogin} />
  )
}

The log in works fine and I can retrieve the users details and even see auth state change in this useEffect

useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      console.log("Auth state changed:", user);
    });
  
    return unsubscribe;
  }, [auth]);

But when I refresh the app, auth is === null and I need to log in again.

Apparently as of firebase >v9 it should auto persist the auth so I shouldn't need to configure anything else?

How can I make my RN app have persisted log in using Firebase?

Package versions:

"firebase": "^11.3.1",
"react": "18.3.1",
"expo": "~52.0.31"

I am using React Native / Expo to build an app with auth. I can log in fine but when I refresh the app, the auth is lost and I need to log in again.

My firebase config looks like this:

import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';

const firebaseConfig = {
  apiKey: "<key>",
  authDomain: "<name>.firebaseapp",
  projectId: "<id>",
  storageBucket: "<id>.appspot",
  messagingSenderId: "<sender_id>",
  appId: "<app_id>",
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);

export { auth };

I have a self hosted log in form in my app that is as so

import { getAuth, signInWithEmailAndPassword } from 'firebase/auth';

const LoginScreen = () => {
  const auth = getAuth();
  
  const handleLogin = useCallback(async (email: string, password: string) => {
    const user = await signInWithEmailAndPassword(auth, email, password);
  }, [auth]);

  return (
    <LoginForm onLogin={handleLogin} />
  )
}

The log in works fine and I can retrieve the users details and even see auth state change in this useEffect

useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      console.log("Auth state changed:", user);
    });
  
    return unsubscribe;
  }, [auth]);

But when I refresh the app, auth is === null and I need to log in again.

Apparently as of firebase >v9 it should auto persist the auth so I shouldn't need to configure anything else?

How can I make my RN app have persisted log in using Firebase?

Package versions:

"firebase": "^11.3.1",
"react": "18.3.1",
"expo": "~52.0.31"
Share Improve this question edited Feb 15 at 18:21 Frank van Puffelen 600k85 gold badges889 silver badges859 bronze badges Recognized by Mobile Development Collective and Google Cloud Collective asked Feb 15 at 16:13 Stretch0Stretch0 9,26315 gold badges92 silver badges157 bronze badges 9
  • Instead of calling getAuth() in LoginScreen, can you try importing auth exported from the Firebase config file and use it in the onAuthStateChanged and useEffect calls? – Dharmaraj Commented Feb 15 at 16:29
  • @Dharmaraj just tried it but doesn't seem to work. Does the same behaviour as I mentioned in my original description – Stretch0 Commented Feb 15 at 16:38
  • The auth exported from my firebase config is coming from getAuth any way so would effectively be the same thing – Stretch0 Commented Feb 15 at 16:38
  • I've seen cases where the config file isn't imported during startup preventing initializeApp from being called. Doing so ensures it the Firebase app is initialized before Login screen is mounted. If that does not work, please share a MVP on Github. – Dharmaraj Commented Feb 15 at 16:40
  • That's a good point. Although I am importing the config file as the first line in my _layout file of Expo so should be the very first thing in the app that gets run when the app loads – Stretch0 Commented Feb 15 at 16:42
 |  Show 4 more comments

1 Answer 1

Reset to default 0

I hope the method below will work.

You can persist the user across states using useContext and/or Async Storage.

Here, I have used Appwrite, but I think Firebase offers a similar method like this.

1. First, you must create a context to provide the user to the entire app. Here, I have used the getCurrentUser() method in Appwrite. I think FIREBASE will also provide a similar method (I think auth.currentUser is the method,but please check this).

import { createContext, useContext, useState, useEffect } from "react";
import { getCurrentUser } from "../lib/appwrite";

const GlobalContext = createContext();

export const useGlobalContext = () => useContext(GlobalContext);

export const GlobalProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    getCurrentUser()
      .then((res) => {
        if (res) {
          setIsLoggedIn(true);
          setUser(res);
        } else {
          setIsLoggedIn(false);
          setUser(null);
        }
      })
      .catch((err) => {
        console.error(err);
      })
      .finally(() => {
        setIsLoading(false);
      });
  }, []);

  return (
    <GlobalContext.Provider
      value={{ isLoggedIn, setIsLoggedIn, user, setUser, isLoading }}
    >
      {children}
    </GlobalContext.Provider>
  );
};
  • As an alternative, you can use AsyncStorage in React Native(If you don't have a built-in method to get the logged-in user)(this is similar to localStorage). So, at the first login, If the user logs in successfully, you can save the user state and other important details (username, token, etc.) in AsyncStorage. When the page refreshes, you have to call AsyncStorage to retrieve that data.

By using the above methods, we can get the logged-in user when the page refreshes.

2. The next thing we must do is ensure that this user state is available throughout our React Native app. So, in _layout.jsx, we should wrap our app using this global provider.

return (
    <GlobalProvider>
      <Stack>
        <Stack.Screen name="index" options={{ headerShown: false }} />
        <Stack.Screen name="(auth)" options={{ headerShown: false }} />
        <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
      </Stack>
    </GlobalProvider>
  );
};

3. The next thing is, you should ensure that your app does not navigate to the login page/splash page when the page refreshes. Instead, it should go to the home page or any other desired page.

index.jsx is the entry point of a React Native app. Here, at first, you just check whether a user is logged in (using our global provider and context). If there is a user, they will be directly navigated to the home page.

import { StatusBar } from "expo-status-bar";
import { Image, ScrollView, StyleSheet, Text, View } from "react-native";
import { Redirect, router } from "expo-router";
import React from "react";
import { SafeAreaView } from "react-native-safe-area-context";

import { images } from "../constants";
import CustomButton from "../components/CustomButton";

import { useGlobalContext } from "../context/GlobalProvider";

export default function App() {
  const { isLoading, isLoggedIn } = useGlobalContext();

  if (!isLoading && isLoggedIn) return <Redirect href="/home" />;

  return (
    <SafeAreaView className="bg-primary h-full">
      <ScrollView contentContainerStyle={{ height: "100%" }}>
        <View className="w-full items-center justify-center h-full px-4">
          <Image
            source={images.logo}
            className="w-[130px] h-[84px]"
            resizeMode="contain"
          />
          <Image
            source={images.cards}
            className="max-w-[300px] h-[300px]"
            resizeMode="contain"
          />

          <View className="relative mt-5">
            <Text className="text-3xl text-white font-bold text-center">
              Discover Endless{"\n"}
              Possibilities with{" "}
              <Text className="text-secondary-200">Aora</Text>
            </Text>

I think by using the above method, you can persist the user when the page refreshes, as well as ensure that the user only need to log in if he log out from the device(Don't need to log in everytime he opens the app ).

发布评论

评论列表(0)

  1. 暂无评论