React Router6 与组件通信:跳转传参、父子 / 跨组件数据共享

3 阅读2分钟

一、react-router6 安装配置

1.1 安装

npm install react-router-dom@6

1.2 数据路由 API(v6.4+ 推荐)

// src/router/index.jsx
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Home from "@/pages/Home";
import About from "@/pages/About";

const router = createBrowserRouter([
  { path: "/", element: <Home /> },
  { path: "/about", element: <About /> },
]);

// main.jsx
import { RouterProvider } from "react-router-dom";
import router from "./router";

createRoot(document.getElementById("root")).render(
  <RouterProvider router={router} />
);

1.3 组件式写法(兼容老习惯)

import { BrowserRouter, Routes, Route } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

v6 相比 v5 的核心变化:SwitchRoutescomponentelement、路由匹配从"全部匹配"变为"排名匹配"。


二、声明式 / 编程式导航

2.1 声明式:Link / NavLink

import { Link, NavLink } from "react-router-dom";

<Link to="/about">关于</Link>
<Link to={`/detail/${id}`}>详情</Link>

{/* NavLink:当前路由自动加 active 类 */}
<NavLink to="/" className={({ isActive }) => (isActive ? "active" : "")}>首页</NavLink>

2.2 编程式:useNavigate

import { useNavigate } from "react-router-dom";

function LoginBtn() {
  const navigate = useNavigate();

  const goLogin = () => navigate("/login");              // 跳转
  const replace = () => navigate("/home", { replace: true });  // 替换不留历史
  const back = () => navigate(-1);                       // 后退

  return <button onClick={goLogin}>去登录</button>;
}

三、useParams / useSearchParams 传参

3.1 路径参数:useParams

// 路由声明
{ path: "/detail/:id", element: <Detail /> }

// 组件内读取
import { useParams } from "react-router-dom";

function Detail() {
  const { id } = useParams();      // URL:/detail/10 → id = "10"
  return <p>详情 ID:{id}</p>;
}

3.2 查询参数:useSearchParams

// 跳转带 query
navigate(`/list?page=2&keyword=react`);

// 组件内读取与修改
import { useSearchParams } from "react-router-dom";

function List() {
  const [searchParams, setSearchParams] = useSearchParams();
  const page = searchParams.get("page") || "1";

  const nextPage = () => {
    setSearchParams({ page: Number(page) + 1, keyword: searchParams.get("keyword") ?? "" });
  };

  return <button onClick={nextPage}>第 {page} 页 → 下一页</button>;
}

3.3 两种传参对比

对比项路径参数 useParams查询参数 useSearchParams
URL 形态/detail/10/list?page=2
读取useParams()useSearchParams().get()
刷新不丢不丢(都在 URL)
适用资源标识(id)筛选、分页、搜索条件

四、父子组件通信

4.1 父传子:props

function Parent() {
  const [count, setCount] = useState(0);
  return <Child count={count} onAdd={() => setCount(count + 1)} />;
}

4.2 子传父:回调函数

function Child({ count, onAdd }) {
  return (
    <div>
      <p>计数:{count}</p>
      <button onClick={onAdd}>+1</button>   {/* 调用父组件传入的回调 */}
    </div>
  );
}

React 无自定义事件:父传子用 props,子传父用回调(等价于 Vue 的 $emit)。


五、跨层通信:useContext

解决"祖先 → 任意后代"的共享数据(主题、登录态、语言等),免去逐层传 props。

5.1 创建与提供

// context/AuthContext.js
import { createContext, useContext, useState } from "react";

const AuthContext = createContext(null);

// Provider:包裹需要共享的子树
export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const login = (u) => setUser(u);
  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// 自定义 Hook:方便使用
export function useAuth() {
  return useContext(AuthContext);
}

5.2 使用

// main.jsx 根部包裹
<AuthProvider>
  <RouterProvider router={router} />
</AuthProvider>
// 任意后代组件
function Profile() {
  const { user, logout } = useAuth();
  if (!user) return <p>未登录</p>;
  return <button onClick={logout}>{user.name} - 退出</button>;
}

注意:Context 值变化会让所有消费组件重渲染;高频变化的状态建议拆分 Context 或配合 useMemo 稳定 value。


六、路由嵌套、404、重定向

6.1 嵌套路由:Outlet

const router = createBrowserRouter([
  {
    path: "/user",
    element: <UserLayout />,        // 父布局
    children: [
      { index: true, element: <UserProfile /> },     // /user 默认子路由
      { path: "orders", element: <UserOrders /> },   // /user/orders
    ],
  },
]);
// UserLayout.jsx:子路由出口
import { Outlet, NavLink } from "react-router-dom";

export default function UserLayout() {
  return (
    <div>
      <nav>
        <NavLink to="/user">资料</NavLink>
        <NavLink to="/user/orders">订单</NavLink>
      </nav>
      <Outlet />     {/* 子路由组件渲染位置 */}
    </div>
  );
}

6.2 重定向与 404

import { Navigate } from "react-router-dom";

const router = createBrowserRouter([
  { path: "/", element: <Navigate to="/home" replace /> },   // 重定向
  // 404 兜底(放最后)
  { path: "*", element: <NotFound /> },
]);

6.3 路由守卫(登录鉴权)

function RequireAuth({ children }) {
  const { user } = useAuth();

  if (!user) {
    return <Navigate to="/login" replace />;   // 未登录跳登录页
  }
  return children;
}

const router = createBrowserRouter([
  {
    path: "/admin",
    element: (
      <RequireAuth>
        <AdminLayout />
      </RequireAuth>
    ),
  },
]);

七、总结

  • 配置createBrowserRouter + RouterProviderRoutes/Route 组件式二选一;
  • 导航Link/NavLink 声明式、useNavigate 编程式;
  • 传参:路径参数 useParams(资源 id)、查询参数 useSearchParams(筛选分页),都在 URL 中刷新不丢;
  • 通信:父传子 props、子传父回调、跨层 useContext(createContext + Provider + useAuth);
  • 路由进阶Outlet 嵌套、Navigate 重定向、path="*" 兜底 404、RequireAuth 包裹实现鉴权。