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

登录后需重载App才显示正确页面的问题排查

问题:登录后需重启App才会切换到认证页面

控制台显示已生成token,但isAuthenticated仍为false,必须重载App才能让状态更新并渲染ExpensesOverview页面。需求是在expenses-context.js中全局管理isAuthenticated状态。


相关代码片段

App.js

function ExpensesOverview() {
  return (
    ...
    <BottomTabs.Screen
      name='RecentExpenses'
      component={RecentExpenses}
    />
    <BottomTabs.Screen
      name='AllExpenses'
      component={AllExpenses}
    />
    </BottomTabs.Navigator>
  )
}

export default function App() {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useEffect(() => {
    const checkAuthentication = async () => {
      try {
        const token = await AsyncStorage.getItem('userToken');
        console.log('Token from AsyncStorage:', token);
        if (token) {
          setIsAuthenticated(true);
        }
      } catch (error) {
        console.error('Error checking authentication:', error);
      }
    };

    checkAuthentication();
  }, []);

  return (
    <>
      <StatusBar style="dark" />
      <ExpensesContextProvider>
        <NavigationContainer>
          <Stack.Navigator>
            {isAuthenticated ? (
              <>
                <Stack.Screen name='ExpensesOverview' component={ExpensesOverview} options={{ headerShown: false }} />
                <Stack.Screen name='ManageExpense' component={ManageExpense} options={{
                  presentation: 'modal'
                }} />
              </>
            ) : (
              <Stack.Screen name="LoginScreen" component={LoginScreen} options={{ headerShown: false }} />
            )}
          </Stack.Navigator>
        </NavigationContainer>
      </ExpensesContextProvider>
    </>  
  );
}

LoginScreen.js

function LoginScreen() {
    const expensesCtx = useContext(ExpensesContext);
    const [credentials, setCredentials] = useState({ email: '', password: '' });

    async function loginHandler({ email, password }) {
        try {
            const response = await expensesCtx.loginUser({ email, password });
            console.log('Response:', response.token);
            
            if (response) {
                const {token} = response;
                console.log('LoginScreen token:', token);
                await AsyncStorage.setItem('userToken', JSON.stringify(token));
                await expensesCtx.updateIsAuthenticated(true);
            } else {
                console.error('Invalid response data:', response);
            }
        } catch (error) {
            console.error('Error logging in:', error);
        }
    }
    

    return (
        <SafeAreaView style={styles.container}>
            <AuthContent isLogin={true} onSignUp={signUpHandler} onLogin={loginHandler} />
            <View style={styles.registerButtonContainer}>
            </View>
        </SafeAreaView>
    );
}

export default LoginScreen;

expenses-context.js

export const ExpensesContext = createContext({
    loginUser: ({ email, password }) => { },
    isAuthenticated: false,
})

function expensesReducer(state, action) {
        case 'LOGIN_USER':
            return {
                ...state,
                isAuthenticated: true,
            }
        default:
            return state;
    }
}

function ExpensesContextProvider({ children }) {
    const [expensesState, dispatch] = useReducer(expensesReducer, []);

    const [isAuthenticated, setIsAuthenticated] = useState(false)

    useEffect(()=>{
        const checkAuthentication = async () => {
            try {
                const token = await AsyncStorage.getItem('email')
                if (token) {
                    setIsAuthenticated(true)
                }
            } catch (error) {
                console.error('Error checking authentication:', error)
            }
        }

        checkAuthentication()
    }, [])
    
    const updateIsAuthenticated = (value) => {
        setIsAuthenticated(value);
    };

    async function loginUser({ email, password }) {
        try {
            const response = await axios.post('http://localhost:8082/authUser', {
                email,
                password,
            });

            const { token } = response.data;
            await AsyncStorage.setItem('userToken', JSON.stringify(token));
            setIsAuthenticated(true);
            dispatch({ type: 'LOGIN_USER', payload: response.data.token });
            return response.data
        } catch (error) {
            console.error('Error logging in:', error);
        }
    }

    const value = {
        loginUser: loginUser,
        isAuthenticated: isAuthenticated,
        updateIsAuthenticated: updateIsAuthenticated,
    };

    return (
        <ExpensesContext.Provider value={value}>
            {children}
        </ExpensesContext.Provider>
    );
}

export default ExpensesContextProvider;

问题根源

  1. 状态分散:App组件自己维护了isAuthenticated状态,和Context中的状态完全独立。登录时更新的是Context里的状态,App组件的状态未同步,因此不会触发页面切换。
  2. 认证检查逻辑错误:Context中启动时读取的是email而非userToken,导致无法正确识别已登录状态。
  3. 冗余操作:LoginScreen重复存储token并手动更新状态,和Context的loginUser功能重叠,且未同步App组件的状态。
  4. Reducer语法缺失:expensesReducer漏写了switch语句,无法正确处理LOGIN_USER action。

解决方案

1. 统一状态管理(修改App.js)

移除App组件自身的状态,直接从Context读取isAuthenticated:

import { useContext } from 'react';
import { ExpensesContext } from './expenses-context';

// ... ExpensesOverview 组件保持不变

export default function App() {
  const { isAuthenticated } = useContext(ExpensesContext);

  return (
    <>
      <StatusBar style="dark" />
      <ExpensesContextProvider>
        <NavigationContainer>
          <Stack.Navigator>
            {isAuthenticated ? (
              <>
                <Stack.Screen name='ExpensesOverview' component={ExpensesOverview} options={{ headerShown: false }} />
                <Stack.Screen name='ManageExpense' component={ManageExpense} options={{ presentation: 'modal' }} />
              </>
            ) : (
              <Stack.Screen name="LoginScreen" component={LoginScreen} options={{ headerShown: false }} />
            )}
          </Stack.Navigator>
        </NavigationContainer>
      </ExpensesContextProvider>
    </>  
  );
}

2. 修复Context逻辑(修改expenses-context.js)

用Reducer统一管理认证状态,修复启动检查逻辑:

import { createContext, useReducer, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import axios from 'axios';

export const ExpensesContext = createContext({
    loginUser: ({ email, password }) => { },
    isAuthenticated: false,
})

function expensesReducer(state, action) {
    switch(action.type) {
        case 'LOGIN_USER':
            return { ...state, isAuthenticated: true };
        case 'CHECK_AUTH':
            return { ...state, isAuthenticated: action.payload };
        default:
            return state;
    }
}

function ExpensesContextProvider({ children }) {
    const [expensesState, dispatch] = useReducer(expensesReducer, { isAuthenticated: false });

    useEffect(()=>{
        const checkAuthentication = async () => {
            try {
                const token = await AsyncStorage.getItem('userToken');
                dispatch({ type: 'CHECK_AUTH', payload: !!token });
            } catch (error) {
                console.error('Error checking authentication:', error);
                dispatch({ type: 'CHECK_AUTH', payload: false });
            }
        }
        checkAuthentication();
    }, []);

    async function loginUser({ email, password }) {
        try {
            const response = await axios.post('http://localhost:8082/authUser', { email, password });
            const { token } = response.data;
            await AsyncStorage.setItem('userToken', JSON.stringify(token));
            dispatch({ type: 'LOGIN_USER' });
            return response.data;
        } catch (error) {
            console.error('Error logging in:', error);
        }
    }

    const value = {
        loginUser: loginUser,
        isAuthenticated: expensesState.isAuthenticated,
    };

    return (
        <ExpensesContext.Provider value={value}>
            {children}
        </ExpensesContext.Provider>
    );
}

export default ExpensesContextProvider;

3. 简化LoginScreen操作(修改LoginScreen.js)

移除冗余的token存储和状态更新:

function LoginScreen() {
    const expensesCtx = useContext(ExpensesContext);
    const [credentials, setCredentials] = useState({ email: '', password: '' });

    async function loginHandler({ email, password }) {
        try {
            const response = await expensesCtx.loginUser({ email, password });
            if (!response) {
                console.error('Invalid response data:', response);
            }
        } catch (error) {
            console.error('Error logging in:', error);
        }
    }
    

    return (
        <SafeAreaView style={styles.container}>
            <AuthContent isLogin={true} onSignUp={signUpHandler} onLogin={loginHandler} />
            <View style={styles.registerButtonContainer}>
            </View>
        </SafeAreaView>
    );
}

export default LoginScreen;

关键修改点

  • 将认证状态完全移至Context的Reducer中,实现全局统一管理
  • 修复Context启动时的token读取逻辑,确保正确识别已登录状态
  • 移除App组件的独立状态,直接依赖Context状态触发页面切换
  • 删除LoginScreen中的冗余操作,所有登录逻辑统一由Context处理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 04:55:01