我为了两个目的使用了两个路由守卫,但在一个特殊的情况下,他们不能正常工作,我认为他们处于竞争状态.

路由

<Router>
  <Routes>
    <Route element={<GlobalGuard />}>
            <Route path="/login" element={<Login />} />

            <Route element={<LocalGuard />}>
                <Route path="/projects/*" element={<Projects />}>
                ...
            </Route>
    </Route>
  </Routes>
</Router>

全球卫士

const dispatch = useDispatch();
const [loading, setLoading] = useState(true);
const [validUser, setValidUser] = useState(false);
const location = useLocation();


useEffect(() => {
    (async () => {
      try {
        if (localStorage.getItem('token')) {
          const localUser = JSON.parse(localStorage.getItem('user'));
          if (localUser && localUser.hasOwnProperty('email')) {
            const pingStatus = await pingUser({ email: localUser.email });
            if (pingStatus == 200) {
              dispatch(fillUser(JSON.parse(localStorage.getItem('user');
              setValidUser(() => true);
              setLoading(() => false);
            } else {
              localStorage.removeItem('user');
              localStorage.removeItem('token');
              setValidUser(() => false);
              setLoading(() => false);
            }
          } else {
            localStorage.removeItem('user');
            localStorage.removeItem('token');
            setValidUser(() => false);
            setLoading(() => false);
          }
        } else {
          localStorage.removeItem('user');
          setValidUser(() => false);
          setLoading(() => false);
        }
      } catch (err) {
        setValidUser(() => false);
        setLoading(() => false);
      }
    })();
  }, [location.pathname]);

console.log(location.pathname); // for checking if render correct

return loading ? <ServerLoad /> : <Outlet context={validUser} />;

地方警卫队

const hasValidJwt = useOutletContext();
console.log(hasValidJwt);

return hasValidJwt ? <Outlet /> : <Navigate to="/login" />;

登录.jsx

const navigate = useNavigate();

const onSubmit = async (data) => {
    try {
      const res = await loginUser(data);
      dispatch(setUser(res));
      localStorage.setItem('token', res.token);
      localStorage.setItem('user', JSON.stringify(res.user));
      navigate('/projects');
    } 
...
  };

当onSubmit代码在登录页面中运行时,在理想情况下,全局防护的useEffect应该使hasValidUser为True,并使页面导航到项目,但实际上它只会刷新.

经过几天的调试,我发现全局警卫的useEffect的控制台.日志(log)在本地警卫的控制台之后运行,它记录false,所以它再次导航到login.即使在hasValidUser设置为true之后,页面在登录时仍会缩水.

有没有人能谈谈他们的观点.

In Short

当我第一次单击LOGIN时,它只用适当的值填充本地存储,第二次它实际上转到/Projects

推荐答案

问题是初始的validUser值与未经身份验证的值相同,因此当LocalGuard 组件挂载并判断validUser值并且该值为假时,将呈现到"/login"的重定向.

实际上有三种"身份验证"状态:"已验证"、"未验证"和"未知".代码应该从"未知"状态开始,并在呈现受保护内容或重定向之前等待身份验证得到确认.

const GlobalGuard = () => {
  const dispatch = useDispatch();
  const { pathname } = useLocation();

  const [loading, setLoading] = useState(true);
  const [validUser, setValidUser] = useState(); // <-- initially undefined


  useEffect(() => {
    (async () => {
      setLoading(true);

      try {
        const localUser = JSON.parse(localStorage.getItem('user'));

        if (!localUser?.email)) {
          throw new Error("no user email");
        }

        const pingStatus = await pingUser({ email: localUser.email });

        if (Number(pingStatus) !== 200) {
          throw new Error("non-200 pingUser status");
        }

        dispatch(fillUser(JSON.parse(localStorage.getItem('user');
        setValidUser(true);
      } catch (error) {
        localStorage.removeItem('user');
        localStorage.removeItem('token');
        setValidUser(false);
      } finally {
        setLoading(false);
      }
    })();
  }, [pathname]);

  return loading ? <ServerLoad /> : <Outlet context={validUser} />;
};
const LocalGuard = () => {
  const hasValidJwt = useOutletContext();

  if (hasValidJwt === undefined) {
    render null; // or loading spinner/indicator/etc
  }

  return hasValidJwt ? <Outlet /> : <Navigate to="/login" replace />;
};

Reactjs相关问答推荐

Npm运行构建`在Next.js 14中不起作用

使用useReducer时,props 未从父级传递给子或孙级

在下一个js中使用ESTAccountWithEmailAndPassword函数时循环

下拉Tailwind CSS菜单与怪异beviour

TanStack/Reaction-Query在我的Vercel应用程序中不起作用

在REACT-RUTER-DOMV6中使用通用页面包装组件有问题吗?

在url中使用MongoDB对象ID被认为是不好的做法?

MUiv5和TSS-Reaction SSR问题:无法可靠地处理样式定义.CSSprops 中的ArrowFunctionExpression

MUI:在props 主题中找到的值:;错误;是[Object],而不是字符串或数字

如何在 React Native 中渲染下面的对象数组?

修改React数据表扩展组件

无法设置 null 的属性(设置src)

有没有办法在用户离开页面时防止输入模糊?

如何到达类组件中的状态?

React Native Realm 应用程序在发布构建后崩溃

vdom 最终更新了 dom(在比较之后)任何 dom 更改实际上应该触发整个树的重绘和回流,那么 vdom 有什么用?

如何在 React JS 上使文本中的单词可点击

列表应该有一个唯一的关键props

我在使用 Elements of stripe 时使用 React router v6

在状态中存储多个选定的选项