redux 检查登录状态以呈现路由(react-router-dom)中组件无法正常工作的逻辑

fzsnzjdm  于 2023-08-05  发布在  React
关注(0)|答案(1)|浏览(117)

我开始学习React,我有一个问题,有关检查用户的登录状态。我通过从localStorage获取用户信息来检查用户的登录状态。如果用户已登录,我将把用户重定向回主页。一切都很好,但如果用户关闭选项卡并返回,如果用户先前登录然后注销,当单击按钮转到认证页面时,即使url设置为"/auth",在这种情况下,似乎loginUser的值是错误的,并且Route呈现<Home />而不是<Auth />。我不明白为什么它会发生在用户关闭标签并返回的情况下。
这是我的App.js代码。

const App = () => {
  const loginUser = JSON.parse(localStorage.getItem("profile"));
  const dispatch = useDispatch();

  // I use redux's global state to store user info and render components corresponding to that state. 
  // So when starting the app check the local storage and sync it with the redux state
  useEffect(() => {
    if (loginUser) {
      dispatch({ type: AUTH, payload: loginUser });
    }
  }, []);

  return (
    <>
      <Helmet>
        <meta
          http-equiv="Cross-Origin-Opener-Policy"
          content="same-origin-allow-popups"
        />
      </Helmet>
      <Router>
        <Container style={{ marginBottom: "150px" }} maxWidth="xl">
          <NavBar />
          <Routes>
            <Route path="/" exact element={<Navigate to={"/posts"} />} />
            <Route path="/posts" exact element={<Home />} />
            <Route path="/posts/search" exact element={<Home />} />
            <Route path="/posts/:id" exact element={<PostDetails />} />
            <Route path="/auth" element={!loginUser ? <Auth /> : <Home />} />
          </Routes>
        </Container>
      </Router>
    </>
  );
};

字符串
下面是我的Navbar,它根据我用来存储登录用户信息的状态“profile”呈现LoginLogout按钮。

export default function NavBar() {
  const classes = useStyle();
  const navigate = useNavigate();
  const dispatch = useDispatch();
  const [messageApi, contextHolder] = message.useMessage();
  const user = useSelector((state) => state.profile);

  const logout = (e) => {
    e.preventDefault();
    dispatch({ type: LOGOUT });
    messageApi.success("Logged out.");
  };

  return (
    <>
      {contextHolder}
      <AppBar className={classes.appBar} position="static" color="inherit">
        <div className={classes.brandContainer}>
          <Typography
            to="/"
            component={Link}
            className={classes.heading}
            variant="h2"
            align="center"
          >
            My app
          </Typography>
          <img
            className={classes.image}
            src={memoriesImg}
            alt="memories"
            height="60"
          />
        </div>
        <Toolbar>
          {Object.keys(user).length !== 0 ? (
            <div className={classes.profile}>
              <Avatar
                className={classes.purple}
                alt={user?.name}
                src={user?.picture}
              >
                {user?.name?.charAt(0)}
              </Avatar>
              <Typography className={classes.userName} variant="h6">
                {user?.name}
              </Typography>
              <Button
                onClick={(e) => logout(e)}
                variant="contained"
                className={classes.logout}
                color="secondary"
              >
                Logout
              </Button>
            </div>
          ) : (
            <Button
              component={Link}
              to="/auth"
              variant="contained"
              color="primary"
            >
              Login
            </Button>
          )}
        </Toolbar>
      </AppBar>
    </>
  );
}


以下是用户登录和注销时reducer内部发生的情况

export default (profile = {}, action) => {
  switch (action.type) {
    case AUTH:
      localStorage.setItem("profile", JSON.stringify({ ...action.payload }));
      return { ...profile, ...action.payload.profile };
    case LOGOUT:
      localStorage.removeItem("profile");
      return {};
    case "TEST":
      console.log("dispatch success TEST from axios");
      return profile;
    default:
      return profile;
  }
};


我还录制了一个简短的视频,其中第一部分显示了当我登录,注销,并正常进入认证页面。但是,当我关闭标签并打开一个新的标签,然后注销后,我不能去认证页面了。
https://somup.com/c0iIQRzu64
我希望有人来检查,如果它是合理的,我存储用户信息,并处理它这样。如果哪里出了什么问题。

8iwquhpp

8iwquhpp1#

问题

基本上,问题是redux状态没有很好地耦合到localStorage。当身份验证状态更改时,App组件不会重新呈现。
以下是事件的时间轴,因为我远,我可以告诉从视频:

  1. App挂载,loginUser从localStorage读取。由于loginUser是falsey,因此不会将任何操作分派到存储区,并且用户未经身份验证。
    1.用户在NavBar中单击“登录”,应用程序导航到"/auth",由于loginUser是错误的,因此呈现Auth组件。
    1.用户身份验证成功,应用程序导航到"/posts"
    1.用户在NavBar中单击“注销”,并成功注销。更新了redux状态,并清除了localStorage。NavBar重新呈现并再次显示“Log In”按钮。
    1.用户在NavBar中单击“登录”,应用程序导航到"/auth",由于loginUser(* 本地副本 仍然 * 错误,因此呈现Auth组件。
    1.用户身份验证成功,应用程序导航到"/posts"
    1.关闭活动选项卡并打开一个新选项卡,挂载App并从localStorage读取loginUser,由于用户已通过身份验证,因此loginUser是真实的,并更新了redux存储。NavBar被重新渲染,并且“注销”按钮被渲染。
    1.用户在NavBar中点击“注销”,成功注销。更新了redux状态,并清除了localStorage。NavBar重新呈现并再次显示“Log In”按钮。
    1.用户在NavBar中单击“Log In”,应用导航到"/auth",由于loginUser(* 本地副本 仍然是 * truthy,因此呈现Home组件。应用程序“卡”在“已验证”状态。
    要解决这个问题,Redux状态应该从localStorage初始化,App中的loginUser应该像NavBar组件中一样从store中选择状态。这样,当Redux store中的auth状态更新时,App将使用当前选定的redux状态值重新呈现。
    示例如下:
const initialState = JSON.parse(localStorage.getItem("profile")) || {};

export default (state = initialState, action) => {
  switch (action.type) {
    case AUTH:
      localStorage.setItem("profile", JSON.stringify({ ...action.payload }));
      return {
        ...state,
        ...action.payload
      };

    case LOGOUT:
      localStorage.removeItem("profile");
      return {};

    case "TEST":
      console.log("dispatch success TEST from axios");
      return state;

    default:
      return state;
  }
};

个字符

相关问题