Skip to content

react-router

react-router

路由权限拦截

组件

tsx
import { useEffect, useMemo } from 'react';
import { useMatches, useOutlet } from 'react-router';

const AuthRoute = () => {
  const matches = useMatches(); //这个钩子只有 data Router 下能用
  const outlet = useOutlet(); //
  const page = useMemo(() => {
    const route = matches?.at(-1);
    console.log('route', route);
    /**
     * 根据路由信息判断路由权限
     * 也可以根据 token 判断, token 不存在, 返回登录页
     * if(!token) {
     *    return <Navigate to="/login" />
     * }
     */
    return outlet;
  }, [matches, outlet]);

  useEffect(() => {
    const title = (matches?.at(-1) as any)?.handle?.title;
    const isHasTitle = typeof title === 'string';
    if (isHasTitle) {
      document.title = title;
    }
  }, [matches]);
  return page;
};
export default AuthRoute;

使用

tsx
export const router = createBrowserRouter([
  {
    path: '/',
    element: <AuthRoute></AuthRoute>, // 所有路径都经过AuthRoute
    children: [],
  },
]);

路由懒加载

hooks

tsx
import React from 'react';
export function useLazy(callback: () => Promise<{ default: React.ComponentType }>) {
  const LazyComp = React.lazy(callback);
  return (
    <React.Suspense fallback={<div>loading...</div>}>
      <LazyComp />
    </React.Suspense>
  );
}

使用

tsx
export const router = createBrowserRouter([
  {
    path: '/',
    element: <AuthRoute></AuthRoute>,
    children: [
      {
        path: '/',
        element: useLazy(() => import('xxxx')),
      },
    ],
  },
]);

路由缓存

组件

tsx
import { createContext, useContext } from 'react';
import { matchPath, useLocation, useOutlet } from 'react-router';

const keepElements = {};

export type KeepAliveContextType = {
  keepPaths: any[];
  keepElements: Record<any, any>;
  dropByPath: (path: any) => void;
};

export const KeepAliveContext = createContext<KeepAliveContextType>({
  keepPaths: [],
  keepElements,
  dropByPath(path) {
    keepElements[path] = null;
  },
});

const isKeepPath = (keepPaths, path) => {
  let isKeep = false;
  for (let i = 0; i < keepPaths.length; i++) {
    const item = keepPaths[i];
    if (item === path) {
      isKeep = true;
    }
    if (item instanceof RegExp && item.test(path)) {
      isKeep = true;
    }
    if (typeof item === 'string' && item.toLowerCase() === path) {
      isKeep = true;
    }
  }
  return isKeep;
};

export function useKeepOutlet() {
  const location = useLocation();
  const outlet = useOutlet();
  const { keepElements, keepPaths } = useContext(KeepAliveContext);
  const isKeep = isKeepPath(keepPaths, location.pathname);
  if (isKeep) {
    keepElements[location.pathname] = outlet;
  }
  return (
    <>
      {!isKeep && outlet}
      {Object.entries(keepElements).map(([pathname, element]) => (
        <div style={{ height: '100%', width: '100%' }} key={pathname} hidden={!matchPath(location.pathname, pathname)}>
          {element}
        </div>
      ))}
    </>
  );
}

const KeepAliveLayout = (props: { keepPaths: any[]; [key: string]: any }) => {
  const { keepPaths, ...other } = props;
  const { keepElements, dropByPath } = useContext(KeepAliveContext);
  return <KeepAliveContext.Provider value={{ keepPaths, keepElements, dropByPath }} {...other}></KeepAliveContext.Provider>;
};
export default KeepAliveLayout;

使用

src/App.tsx

tsx
const App = () => {
  return (
    <KeepAliveLayout keepPaths={[]}>
      <RouterProvider router={router}></RouterProvider>
    </KeepAliveLayout>
  );
};

export default App;

src/layouts/index.tsx

tsx
const LayoutComponent = () => {
  const outlet = useKeepOutlet();
  return <div className="size-full">{outlet}</div>;
};
export default LayoutComponent;