必须重新加载应用程序才能在登录(身份验证)后获得正确的屏幕

问题描述 投票:0回答:1

在我的

App.js
中,
{isAuthenticated ? ( ExpensesOverview) : ( LoginScreen)
的逻辑并没有像我希望的那样工作。控制台日志显示已生成令牌,但
isAuthenticated
仍为 false。为什么我必须重新加载应用程序,以便
isAuthenticated
为 true 并且
ExpensesOverview
屏幕最终呈现?

我想要的是在

isAuthenticated
中管理整个应用程序的
expenses-context.js
状态。
App.js
LoginScreen.js
expenses-context.js
的相关部分如下:

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(() => {
    // Check if the user is authenticated (e.g., token exists)
    const checkAuthentication = async () => {
      try {
        const token = await AsyncStorage.getItem('userToken');
        console.log('Token from AsyncStorage:', token); // Log the token value
        if (token) {
          setIsAuthenticated(true);
        }
      } catch (error) {
        console.error('Error checking authentication:', error);
        // setIsAuthenticated(false); // Set to false on 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>
    </>  
  );
}

登录屏幕.js:

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

    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); // Set isAuthenticated using the context function
            } else {
                console.error('Invalid response data:', response);
                // Handle the case where the response data is not as expected
            }

            
        } 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 }) => { }, // New loginUser function
    isAuthenticated: false, // Add an initial isAuthenticated state
})

function expensesReducer(state, action) {
        case 'LOGIN_USER':
            // Add the new user to the state (if needed)
            // return [{ ...action.payload }, ...state];
            return {
                ...state,
                isAuthenticated: true,
            }
        default:
            return state;
    }
}

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

    // auth status
    const [isAuthenticated, setIsAuthenticated] = useState(false)


    // check user auth and update the state
    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);
    };

    // loginUser: log in a user
    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); // Set isAuthenticated to true upon successful login
            dispatch({ type: 'LOGIN_USER', payload: response.data.token });
            return response.data
        } catch (error) {
            console.error('Error logging in:', error);
        }
    }

    const value = {
        loginUser: loginUser, // Include the loginUser function
        isAuthenticated: isAuthenticated, // Include the isAuthenticated state
        updateIsAuthenticated: updateIsAuthenticated,
    };

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

export default ExpensesContextProvider;
javascript reactjs react-native react-navigation
1个回答
1
投票

问题

问题是你有两种

isAuthenticated
状态,一种处于
App.js
,另一种处于
expenses-context.js
。登录后更新最后一条,但只是根据里面的状态更新你的路线
App.js

解决方案

解决问题的一种方法不会对当前代码进行太大改变,是将

App
包装在上下文提供程序中,如下所示:

// 👇🏽 notice the default export is removed and added back below

function App() {
  const { isAuthenticated, updateIsAuthenticated } = useContext(ExpensesContext);
  const { checking, setChecking } = useState(true);

  useEffect(() => {
    if(!checking) return;

    // Check if the user is authenticated (e.g., token exists)
    const checkAuthentication = async () => {
      try {
        const token = await AsyncStorage.getItem("userToken");
        console.log("Token from AsyncStorage:", token); // Log the token value
        if (token) {
          updateIsAuthenticated(true);
        }
      } catch (error) {
        console.error("Error checking authentication:", error);
        updateIsAuthenticated(false); // Set to false on error
      } finally {
        setChecking(false);
      }
    };

    checkAuthentication();
  }, [updateIsAuthenticated, checking]);

  if (checking) {
    return "Loading....";
  }

  // 👇🏽 notice that ExpensesContextProvider is removed, and added below

  return (
    <>
      <StatusBar style="dark" />
      <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>
    </>
  );
}

// 👇🏽 the default export is added here

export default function AppWithContext() {
  return (
    <ExpensesContextProvider>
      <App />
    </ExpensesContextProvider>
  );
}
© www.soinside.com 2019 - 2024. All rights reserved.