You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native登录/登出状态不自动更新,需刷新应用的问题排查

问题:React Native登录/登出后需刷新才能跳转页面

我有一个集成登录、登出功能的React Native应用,功能可正常运行,但完成登录或登出操作后,必须刷新应用才能跳转到对应页面,怀疑是代码导致页面无法自动渲染切换,相关代码如下:

StackNavigator.js:

import React from "react";
import { createStackNavigator } from "@react-navigation/stack";
import { HomeScreen, LoginScreen, RegistrationScreen } from "../screens";
import useAuth from "../hooks/useAuth";

const Stack = createStackNavigator();

const StackNavigator = () => {
  const { user } = useAuth();
  return (
    <Stack.Navigator>
      {user ? ( // if user is logged in we show the home screen
        <Stack.Screen name="Home" component={HomeScreen} />
      ) : (
        <>
          <Stack.Screen name="Login" component={LoginScreen} />
          <Stack.Screen name="Registration" component={RegistrationScreen} />
        </>
      )}
    </Stack.Navigator>
  );
};

export default StackNavigator;

useAuth.js:

import React, { createContext, useContext, useEffect, useState } from "react";
import { auth } from "../model/config";
import {
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
} from "firebase/auth";

const AuthContext = createContext({});

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null); //user is initally null
  const [loadingInital, setLoadingInital] = useState(true); //loadingInital is initally true
  const [loading, setLoading] = useState(false); //loading is initally false

  useEffect(() => {
    //This hook allows us to remember the user's login status
    const unsub = auth.onAuthStateChanged((user) => {
      if (user) {
        setUser(user);
      } else {
        setUser(null);
      }
      setLoadingInital(false); //after user is fetched, loadingInital is set to false
    });
    return unsub(); //unsubscribe from the auth listener
  }, []);

  const onLoginPress = (email, password) => {
    setLoading(true); //loading is set to true
    //Takes in two arguments, email and password used in LoginScreen class
    signInWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        // Signed in

        const user = userCredential.user;
        console.warn("signed in");
        console.warn(user);
        //Navigate after sign in
        // ...
      })
      .catch((error) => {
        //If any error we will catch
        const errorCode = error.code;

        if (errorCode === "auth/user-not-found") {
          console.warn("User not found");
        }

        if (errorCode === "auth/wrong-password") {
          console.warn("Wrong password");
        } else {
          console.warn(error);
        }
      });
    setLoading(false); //loading is set to false
  };
  const onRegisterPress = (email, password, confirmPassword) => {
    setLoading(true); //loading is set to true
    if (password !== confirmPassword) {
      alert("Passwords don't match.");
      return;
    }
    createUserWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        const user = userCredential.user;

        alert("Welcome");
      })
      .catch((error) => {
        alert(error);
      });
    setLoading(false); //loading is set to false
  };

  const signOut = () => {
    setLoading(true); //loading is set to true
    auth.signOut();
    console.warn(user);
    setLoading(false); //loading is set to false
  };

  return (
    <AuthContext.Provider
      value={{ user, onLoginPress, onRegisterPress, signOut, loading }}
    >
      {!loadingInital && children}
    </AuthContext.Provider>
  );
};
//if loadingInital is true, we will render nothing ^

export default function useAuth() {
  return useContext(AuthContext);
}

LoginScreen:

import React, { useState } from "react";
import { Image, Text, TextInput, TouchableOpacity, View } from "react-native";
import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
import styles from "./styles"; //styles
import FINAL_STYLES from ".././../FINAL_STYLES"; //styles main
import { auth } from "../../model/config";
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import useAuth from "../../hooks/useAuth";

export default function LoginScreen({ navigation }) {
  const { onLoginPress } = useAuth();
  const [email, setEmail] = useState(""); //setting as temp state
  const [password, setPassword] = useState("");

  const onFooterLinkPress = () => {
    navigation.navigate("Registration");
  };

  //Below we return JSX
  return (
    <View style={styles.container}>
      <KeyboardAwareScrollView
        style={{ flex: 1, width: "100%" }}
        keyboardShouldPersistTaps="always"
      >
        <Image
          style={styles.logo}
          source={require("../../../assets/icon.png")}
        />
        <TextInput
          style={styles.input}
          placeholder="E-mail"
          placeholderTextColor="#aaaaaa"
          onChangeText={(text) => setEmail(text)}
          value={email}
          underlineColorAndroid="transparent"
          autoCapitalize="none"
        />
        <TextInput
          style={styles.input}
          placeholderTextColor="#aaaaaa"
          secureTextEntry
          placeholder="Password"
          onChangeText={(text) => setPassword(text)}
          value={password}
          underlineColorAndroid="transparent"
          autoCapitalize="none"
        />
        <TouchableOpacity
          style={[styles.button, { backgroundColor: "#9D3BEA" }]} //TODO: change to global styles
          onPress={() => onLoginPress(email, password)}
        >
          <Text style={styles.buttonTitle}>Log in</Text>
        </TouchableOpacity>
        <View style={styles.footerView}>
          <Text style={styles.footerText}>
            Don't have an account?{" "}
            <Text onPress={onFooterLinkPress} style={styles.footerLink}>
              Sign up
            </Text>
          </Text>
        </View>
      </KeyboardAwareScrollView>
    </View>
  );
}

修复方案

1. 异步操作的状态更新时机错误

当前代码中setLoading(false)直接写在异步方法外部,会导致登录/登出操作还未完成就提前更新状态,同时没有保证user状态更新的时机。需要将setLoading(false)移到异步回调的then和catch内部,确保操作完成后再更新状态。

2. 修改后的useAuth.js关键代码

const onLoginPress = (email, password) => {
  setLoading(true);
  signInWithEmailAndPassword(auth, email, password)
    .then((userCredential) => {
      const user = userCredential.user;
      console.warn("signed in");
      console.warn(user);
      setLoading(false); // 操作完成后更新状态
    })
    .catch((error) => {
      const errorCode = error.code;
      if (errorCode === "auth/user-not-found") {
        console.warn("User not found");
      } else if (errorCode === "auth/wrong-password") {
        console.warn("Wrong password");
      } else {
        console.warn(error);
      }
      setLoading(false); // 错误时也更新状态
    });
};

const onRegisterPress = (email, password, confirmPassword) => {
  setLoading(true);
  if (password !== confirmPassword) {
    alert("Passwords don't match.");
    setLoading(false); // 提前返回时更新状态
    return;
  }
  createUserWithEmailAndPassword(auth, email, password)
    .then((userCredential) => {
      const user = userCredential.user;
      alert("Welcome");
      setLoading(false); // 操作完成后更新状态
    })
    .catch((error) => {
      alert(error);
      setLoading(false); // 错误时更新状态
    });
};

const signOut = () => {
  setLoading(true);
  auth.signOut()
    .then(() => {
      console.warn("user signed out");
      setLoading(false); // 登出完成后更新状态
    })
    .catch((error) => {
      console.warn(error);
      setLoading(false); // 错误时更新状态
    });
};

3. 原理说明

  • auth.onAuthStateChanged监听用户登录状态变化,当登录/登出操作完成后,会自动触发回调更新user状态
  • StackNavigator组件依赖user状态进行条件渲染,user状态变化会触发组件重新渲染,自动切换到对应页面
  • 修正异步操作的状态更新时机后,确保user状态的更新能被正确捕获,避免页面无法自动切换的问题

内容的提问来源于stack exchange,提问作者songexile9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 14:15:44