ReactJS:在index层使用createBrowserRouter并保留App内路由
问题描述
使用useBlocker时抛出错误:Error: useBlocker must be used within a data router,需将BrowserRouter替换为createBrowserRouter,但希望路由配置仍保留在App.tsx中,如何修改?
原代码结构
index.tsx
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { BrowserRouter } from 'react-router-dom'; import { Provider as ReduxProvider } from "react-redux"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <ReduxProvider store={store}> <BrowserRouter> <App /> </BrowserRouter> </ReduxProvider> </React.StrictMode> );
函数式App组件
import {Routes, Route} from "react-router-dom"; import About from "./component/About"; import Team from "./component/Team"; import Home from "./component/Home"; function App() { return ( <> <Navbar /> <Routes > <Route path="/" element={ <Home /> } /> <Route path="/team" element={ <Team /> } /> <Route path="/about" element={ <About /> } /> </Routes> </> ); } export default App;
withRouter Hook
import { Location, NavigateFunction, Params, useLocation, useNavigate, useParams, } from "react-router-dom"; export interface RouterProps { location: Location; navigate: NavigateFunction; params: Params; } export interface WithRouter { router: RouterProps; } function withRouter(Component: any) { function ComponentWithRouterProp(props: any) { const location = useLocation(); const navigate = useNavigate(); const params = useParams(); return <Component {...props} router={{ location, navigate, params }} />; } return ComponentWithRouterProp; } export default withRouter;
类组件App
class App extends React.Component<IProps, IState> { constructor(props: IProps) { super(props); } render() { return ( <div className='container'> <Navbar /> <Suspense> <Routes> <Route path="/" element={ <Home /> } /> <Route path="/team" element={ <Team /> } /> <Route path="/about" element={ <About /> } /> </Routes> </Suspense> </div> ); } } export default withRouter(App);
解决方案
函数式组件版本
步骤1:修改index.tsx
移除BrowserRouter,改用createBrowserRouter和RouterProvider,复用App中的路由配置:
import React from 'react'; import ReactDOM from 'react-dom/client'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import App, { routes } from './App'; import { Provider as ReduxProvider } from "react-redux"; // 创建数据路由,将App作为父路由,子路由复用App中的配置 const router = createBrowserRouter([ { path: "/", element: <App />, children: routes, }, ]); const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <ReduxProvider store={store}> <RouterProvider router={router} /> </ReduxProvider> </React.StrictMode> );
步骤2:调整App.tsx
抽离路由配置为单独数组,用Outlet渲染子路由内容,保留原有布局结构:
import { Outlet, RouteObject } from "react-router-dom"; import About from "./component/About"; import Team from "./component/Team"; import Home from "./component/Home"; // 抽离路由配置,供index.tsx引用 export const routes: RouteObject[] = [ { path: "/", element: <Home /> }, { path: "/team", element: <Team /> }, { path: "/about", element: <About /> }, ]; function App() { return ( <> <Navbar /> {/* Outlet负责渲染子路由对应的组件 */} <Outlet /> </> ); } export default App;
类组件版本
步骤1:修改index.tsx
与函数式版本一致,替换为数据路由:
import React from 'react'; import ReactDOM from 'react-dom/client'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import App, { routes } from './App'; import { Provider as ReduxProvider } from "react-redux"; const router = createBrowserRouter([ { path: "/", element: <App />, children: routes, }, ]); const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <ReduxProvider store={store}> <RouterProvider router={router} /> </ReduxProvider> </React.StrictMode> );
步骤2:调整类组件App.tsx
同样抽离路由配置,用Outlet替代原Routes组件:
import React, { Suspense } from 'react'; import { Outlet, RouteObject } from "react-router-dom"; import About from "./component/About"; import Team from "./component/Team"; import Home from "./component/Home"; import withRouter from './withRouter'; export const routes: RouteObject[] = [ { path: "/", element: <Home /> }, { path: "/team", element: <Team /> }, { path: "/about", element: <About /> }, ]; interface IProps { // 保留原有Props类型定义 } interface IState { // 保留原有State类型定义 } class App extends React.Component<IProps, IState> { constructor(props: IProps) { super(props); } render() { return ( <div className='container'> <Navbar /> <Suspense fallback={<div>加载中...</div>}> {/* 使用Outlet渲染子路由内容 */} <Outlet /> </Suspense> </div> ); } } export default withRouter(App);
关键说明
useBlocker是React Router v6.4+数据路由专属API,必须配合createBrowserRouter和RouterProvider使用,无法与传统BrowserRouter兼容。- 通过
Outlet组件可以在父路由组件中渲染子路由内容,既保留了App.tsx中的布局(Navbar),又能将路由配置集中在同一文件。 - 类组件的
withRouter无需修改,在数据路由环境下依然可以正常获取路由相关hooks的返回值。
内容的提问来源于stack exchange,提问作者lrr59
相关产品推荐
相关产品推荐

