5.ReactRouter路由与Redux状态管理
一、ReactRouter 路由
1、ReactRouter 基础路由介绍
- 安装 ReactRouter
npm i react-router-dom
- 在
/src/router/index.ts
中创建路由配置
//createBrowserRouter: history模式 createBrowserRouter: hash模式
import { createBrowserRouter } from "react-router-dom";
import type { RouteObject } from "react-router-dom";
import Home from "../views/Home";
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
},
]);
export default router;
- 在
/src/index.tsx
中配置路由
路由生效组件
<RouterProvider router={router} />
import { RouterProvider } from "react-router-dom";
import router from "./router";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
);
root.render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
/src/views/Home/Home.tsx
import React from "react";
const Home = () => {
return <div>Home</div>;
};
export default Home;
2、React Router v6 的嵌套路由
- 在
src/views/Layout/Layout.tsx
中创建布局组件,引入Outlet
组件
import React from "react";
// Outlet组件用于显示子路由
import { Outlet } from "react-router-dom";
import Home from "../Home";
import User from "../User";
const Layout = () => {
return (
<div>
<div className="slider">菜单</div>
<div className="content">
<Outlet />
</div>
</div>
);
};
export default Layout;
- 新建
src/views/User/User.tsx
import React from "react";
const User = () => {
return <div>User</div>;
};
export default User;
- 在
src/router/index.ts
中配置子路由
import { createBrowserRouter } from "react-router-dom";
import type { RouteObject } from "react-router-dom";
import Home from "../views/Home";
import User from "../views/User";
import Layout from "../views/Layout";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
path: "",
element: <Home />,
},
{
path: "user",
element: <User />,
},
],
},
]);
export default router;
3、路由表组件写法
src/router/index.ts
import {
Route,
createRoutesFromElements,
createBrowserRouter,
} from "react-router-dom";
import Home from "../views/Home/Home";
import About from "../views/About/About";
import App from "../App";
const routes = createRoutesFromElements(
<Route path="/" element={<App />}>
<Route path="" element={<Home />}></Route>
<Route path="about" element={<About />}></Route>
</Route>
);
const router = createBrowserRouter(routes);
export default router;
4、声明式路由,路由跳转<Link to="" />
src/views/Layout/Layout.tsx
import React from "react";
import { Link, Outlet } from "react-router-dom";
import Home from "../Home";
import User from "../User";
const Layout = () => {
return (
<div>
<div className="slider">菜单</div>
<div className="content">
<Outlet />
<Link to="/">Home</Link>
<Link to="/user">User</Link>
</div>
</div>
);
};
export default Layout;
二、动态路由模式模式与编程式路由模式
1、动态路由:path:/user/:id
与useParams
函数
src/router/index.js
import { createBrowserRouter } from "react-router-dom";
import App from "../App.jsx";
import User from "../views/User/User";
import UserInfo from "../views/UserInfo/UserInfo";
// 路由表
const routes = [
{
path: "/",
element: <App />,
children: [
{
path: "user",
element: <User />,
children: [
{
path: "userinfo/:id", // 动态路由
element: <UserInfo />,
},
],
},
],
},
];
const router = createBrowserRouter(routes);
export default router;
src/views/User/User.jsx
import "./User.scss";
import { Outlet, Link } from "react-router-dom";
export default function User() {
return (
<>
<div></div>
<Link to="/user/userinfo/123">userInfo123</Link> ||
<Link to="/user/userinfo/456">userInfo456</Link>
<Outlet />
</>
);
}
src/views/UserInfo/UserInfo.jsx
import "./UserInfo.scss";
import { useParams } from "react-router-dom";
export default function UserInfo() {
// 获取动态路由参数
const params = useParams();
return <div>UserInfo,{params.id}</div>;
}
2、带样式的声明式路由 NavLink
NavLink 组件默认会添加active
类名,样式需要自己添加
src/views/User/User.jsx
import "./User.scss";
import { Outlet, NavLink } from "react-router-dom";
export default function User() {
return (
<>
<div></div>
<NavLink to="/user/userinfo/123">userInfo123</NavLink> ||
<NavLink to="/user/userinfo/456">userInfo456</NavLink>
<Outlet />
</>
);
}
src/views/User/User.scss
.active {
color: aqua;
}
如果需要默认设置选择和未选中样式,可以在NavLink
组件上设置activeClassName
和activeStyle
属性
src/views/User/User.jsx
import React from "react";
import "./User.scss";
import { Outlet, NavLink } from "react-router-dom";
export default function User() {
return (
<>
<div></div>
<NavLink
className={({ isActive }) => (isActive ? "curSelect1" : "none")}
to="/user/userinfo/123"
>
userInfo123
</NavLink>
||
<NavLink
className={({ isActive }) => (isActive ? "curSelect2" : "none")}
to="/user/userinfo/456"
>
userInfo456
</NavLink>
<Outlet />
</>
);
}
src/views/User/User.scss
.curSelect1 {
color: aqua;
}
.curSelect2 {
color: pink;
}
3、编程式路由跳转useNavigate
useNavigate 可以在组件内进行路由跳转
src/views/User/User.jsx
import React from "react";
import "./User.scss";
import { Outlet, useNavigate } from "react-router-dom";
export default function User() {
const navigate = useNavigate();
const handleClick = () => {
navigate("/user/userinfo/123");
};
return (
<>
<div></div>
<button onClick={handleClick}>click to userinfo</button>
<Outlet />
</>
);
}
三、useLocation
与 useSearchParams
1、useLocation 可以获取当前路由信息
其中有location.hash
表示当前路由的哈希值,location.pathname
表示当前路由的路径名,location.search
表示当前路由的查询参数,location.state
表示当前路由的额外信息,location.key
表示当前路由的 key
src/views/User/User.jsx
import React from "react";
import "./User.scss";
import { Outlet, useNavigate } from "react-router-dom";
export default function User() {
const navigate = useNavigate();
const handleClick = () => {
navigate("/user/userinfo/12?34#56", { state: { foo: 789 } });
};
return (
<>
<div></div>
<button onClick={handleClick}>click to userinfo</button>
<Outlet />
</>
);
}
在src/views/UserInfo/UserInfo.jsx
中使用 useLocation
import "./UserInfo.scss";
import { useLocation } from "react-router-dom";
export default function UserInfo() {
const location = useLocation();
console.log(location, "useLocation");
return <div>UserInfo</div>;
}
显示的结果是
{
hash: "#56",
pathname: "/user/userinfo/123",
search: "?34",
state: { foo: 789 },
key: "63o4wd7k"
}
2、useSearchParams 可以获取当前路由的查询参数,如获取?username=xiaomu
中的xiaomu
src/views/User/User.jsx
import React from "react";
import "./User.scss";
import { Outlet, useNavigate } from "react-router-dom";
export default function User() {
const navigate = useNavigate();
const handleClick = () => {
navigate("/user/userinfo/123?username=xiaobu");
};
return (
<>
<div></div>
<button onClick={handleClick}>click to userinfo</button>
<Outlet />
</>
);
}
在src/views/UserInfo/UserInfo.jsx
中使用useSearchParams
,通过searchParams.get('username')
获取和通过setSearchParams({age:18})
更改当前路由的查询参数
import "./UserInfo.scss";
import { useSearchParams } from "react-router-dom";
export default function UserInfo() {
const [searchParams, setSearchParams] = useSearchParams();
console.log(searchParams.get("username"));
const handleClick = () => {
setSearchParams({ age: 18 });
};
return (
<>
<div>UserInfo</div>
<button onClick={handleClick}>修改username</button>
</>
);
}
四、默认路由展示与重定向路由与 404 处理
1、默认路由展示
默认路由:{index:true,element:
在src/views/About/About.jsx
通过Outlet
展示路由
import React from "react";
import "./About.scss";
import { Outlet } from "react-router-dom";
export default function About() {
return (
<>
<div>About</div>
<Outlet />
</>
);
}
import { createBrowserRouter } from "react-router-dom";
import App from "../App.jsx";
import Home from "../views/Home/Home";
import About from "../views/About/About";
const routes = [
{
path: "/",
element: <App />,
children: [
{
path: "/home",
element: <Home />,
},
{
path: "/about",
element: <About />,
// 默认路由展示
children: [
{
index: true,
element: <div>默认页面展示</div>,
},
],
},
],
},
];
const router = createBrowserRouter(routes);
export default router;
2、重定向路由
重定向路由:{index:true,element:}
3、404 处理
errorElement:
const routes = [
{
path: "/",
element: <App />,
errorElement: <div>404</div>,
},
];
若是只在某一个页面下进行 404 处理,可以这样写
const routes = [
{
path: "/",
element: <App />,
children: [
{
path: "/home",
element: <Home />,
},
{
path: "/about",
element: <About />,
children: [
{
path:'*'
element: <div>404</div>,
},
],
},
],
},
];
五、loader 函数与 redirect 方法
1、loader 函数
- loader 函数进行路由前触发,配合 redirect 做权限拦截
- useLoaderData()获取 loader 函数返回的数据
在src/router/index.js
中使用 loader 函数
const routes = [
{
path: "/",
element: <App />,
errorElement: <div>404</div>,
children: [
{
path: "/home",
element: <Home />,
// loader 函数进行路由前触发
loader: async () => {
console.log("loader");
// 经过一秒后返回数据
let res = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ errorcode: 0 });
}, 1000);
});
return res;
},
},
],
},
];
在useLoaderData
中获取 loader 函数返回的数据
import React from "react";
import "./Home.scss";
import { useLoaderData } from "react-router-dom";
export default function Home() {
const data = useLoaderData();
console.log(data); // {errorcode:0}
return <div>Home</div>;
}
2、redirect 方法
redirect 方法进行路由后触发,配合 loader 做权限拦截
创建一个
BeforeEach
包裹根组件自定义元信息
meta
组件中获取
meta
信息:matchRoutes
与useLocation
src/components/BeforeEach/BeforeEach.jsx
import React from "react";
import { useLocation, matchRoutes, Navigate } from "react-router-dom";
import { routes } from "../../router";
export default function BeforeEach(props) {
const location = useLocation();
const matchs = matchRoutes(routes, location);
// 获取当前路由的元信息
const { auth } = matchs[matchs.length - 1].route.meta;
if (auth) {
return <Navigate to="/login" />;
} else {
return <div>{props.children}</div>;
}
}
在src/router/index.js
中使用 BeforeEach 组件
import { createBrowserRouter } from "react-router-dom";
import App from "../App.jsx";
import Home from "../views/Home/Home";
import Login from "../views/Login/Login";
import About from "../views/About/About";
import BeforeEach from "../components/BeforeEach/BeforeEach";
// 路由表
export const routes = [
{
path: "/",
element: (
<BeforeEach>
<App />
</BeforeEach>
),
errorElement: <div>404</div>,
children: [
{
path: "/",
element: <Home />,
meta: { title: "首页", auth: false },
},
{
path: "/login",
element: <Login />,
meta: { title: "登录", auth: false },
},
{
path: "/about",
element: <About />,
meta: { title: "关于", auth: true },
},
],
},
];
const router = createBrowserRouter(routes);
export default router;