注销 React 应用程序会导致主屏幕出现空指针

Logging out of react app results in nullpointer in homescreen

我正在开发一个带有登录页面的 React 应用程序。当用户成功登录时,后端 returns 一个用户对象 (JSON),包含一些用户属性(id、用户名...)以及 JWT 令牌。用户对象存储在 AsyncStorage 中(因此用户只需登录一次)。

try {
   await AsyncStorage.setItem('authUser', JSON.stringify(data));
} catch (e) {
   console.log(e.message);
}

dispatch({ type: 'SIGN_IN', authUser:data});

状态:

const [state, dispatch] = React.useReducer(

        (prevState, action) => {
          switch (action.type) {
            case 'RESTORE_TOKEN':
              return {
                ...prevState,
                authUser: action.authUser,
                isLoading: false,
              };
            case 'SIGN_IN':
              return {
                ...prevState,
                isSignout: false,
                authUser: action.authUser,
              };
            case 'SIGN_OUT':
              return {
                ...prevState,
                isSignout: true,
                authUser: null,
              };
          }
        },
        {
          isLoading: true,
          isSignout: false,
          authUser: "",
        }
    );

我第二次从 AsyncStorage 检索用户对象并将其传递给状态。

try {
   authUser = await AsyncStorage.getItem('authUser');
} catch (e) {
   console.log(e.message);
}
dispatch({ type: 'RESTORE_TOKEN', authUser: JSON.parse(authUser) });

我使用 UserContext 将 authUser 传递到我的欢迎屏幕:

<AuthContext.Provider value={authContext}>
   <UserContext.Provider value={{authUser: state.authUser }}>
      <NavigationContainer>
         <RootStack.Navigator mode="modal" headerMode="none">
            {state.isLoading ? (
               <RootStack.Screen name="Splash" component={SplashScreen} />
            ) : state.authUser == null ? (
               <RootStack.Screen name="Login" component={AuthenticationStackScreen} />
            ) : (
               <RootStack.Screen name="Home" component={HomeScreen} />
            )}
         </RootStack.Navigator>
      </NavigationContainer>
   </UserContext.Provider>
</AuthContext.Provider>

在我的主屏幕上,我打印了一条包含用户名和 signOut 按钮的欢迎消息:

function HomeScreen(props) {

    const { signOut } = React.useContext(AuthContext);
    const { authUser } = React.useContext(UserContext);

    return (

        <View style={{ flex:1, alignItems: 'center', justifyContent: 'center', backgroundColor:'cyan' }}>

        <Text>Hallo {authUser.username}</Text>
            <Button title="Log out" onPress={signOut} />
            <Text>HomeScreen </Text>
        </View>

    );
}

当用户登录或已经登录时,主屏幕会正确显示正确的用户名。单击注销按钮时出现问题。 authContext 中的 signOut 方法被调用,它注销用户并从 AsyncStorage 中删除对象:

signOut: async () => {

   try {
      await AsyncStorage.removeItem('authUser');
   } catch (e) {
      console.log(e.message);
   }
   dispatch({ type: 'SIGN_OUT' })
}

问题: 状态更改为 SIGN_OUT,这使我返回到登录页面。但是由于我已经清除了用户对象,欢迎通知会引发错误,因为 {authUser.username}null:

TypeError: null is not an object (evaluating 'authUser.username')

我没想到在单击退出按钮时会重新呈现 WelcomeScreen。我搞砸了我的应用程序的结构,或者这是为什么?任何有关如何解决此问题的帮助将不胜感激。提前致谢!

根据 React documentation useContext 将始终 re-render 当上下文值更改时,这就是再次调用 HomeScreen 的原因。

您需要修复的另一件事是 authUser 初始状态等于 authUser signOut 状态:

const [state, dispatch] = React.useReducer(

        (prevState, action) => {
          switch (action.type) {
            case 'RESTORE_TOKEN':
              return {
                ...prevState,
                authUser: action.authUser,
                isLoading: false,
              };
            case 'SIGN_IN':
              return {
                ...prevState,
                isSignout: false,
                authUser: action.authUser,
              };
            case 'SIGN_OUT':
              return {
                ...prevState,
                isSignout: true,
                authUser: null,
              };
          }
        },
        {
          isLoading: true,
          isSignout: false,
          authUser: "", //MAKE THIS THE SAME AS SIGN_OUT (null)
        }
    );