React 18与React-Router 6中如何在导航栏获取路由参数
如何在React Router v6的
<Routes>外获取路由参数? 问题描述
我有一个React前端项目,需要在<Routes>组件之外的导航菜单(NavMenu类组件)中获取路由参数auxParam,但无法直接使用useParams()。尝试将<Routes>移到<NavMenu>之外时出现报错:
Error: [default] is not a
component. All component children of must be a or <React.Fragment>
React 18之前我用this.props.location.pathname.split...的临时方案,但不够可靠。同时react-router-dom@6已经移除了withRouter高阶组件。
简化后的项目代码如下:
index.tsx
ReactDOM.createRoot(document.getElementById('react-hook')) .render( <Provider store={store}> <BrowserRouter> <App /> </BrowserRouter> </Provider> );
app.tsx
export default () => ( <Layout> <Routes> <Route path='/:auxParam/site/foo/:startIndex?' Component={Extra} /> </Routes> </Layout> );
layout.tsx
export default (props: { children?: React.ReactNode }) => ( <React.Fragment> <NavMenu /> <Container tag="main"> {props.children} </Container> </React.Fragment> );
navmenu.tsx
class NavMenu extends React.PureComponent<NavMenuProps, { isOpen: boolean }> { public render() { const auxParam: string = ...?...; // 如何获取这个参数? return ( <header> <Navbar> <Container> <NavbarBrand tag={Link} to="/">My site (React)</NavbarBrand> <ul> <NavItem> <NavLink tag={Link} to={`/${aux}/site/foo`}>Extra</NavLink> </NavItem> </ul> </Container> </Navbar> </header> ); } } export default NavMenu;
解决方案
1. 自定义withRouter高阶组件
React Router v6官方移除了withRouter,但可以自行封装,让类组件能访问路由相关hooks:
import { useLocation, useNavigate, useParams, useMatch } from 'react-router-dom'; const withRouter = (Component: React.ComponentType) => { const Wrapper = (props: any) => { const location = useLocation(); const navigate = useNavigate(); const params = useParams(); const match = useMatch('/:auxParam/site/foo/:startIndex?'); // 匹配目标路由规则 return <Component {...props} location={location} navigate={navigate} params={params} match={match} />; }; return Wrapper; };
在NavMenu中使用:
class NavMenu extends React.PureComponent<NavMenuProps & { params: { auxParam?: string } }, { isOpen: boolean }> { public render() { const { auxParam } = this.props.params; return ( // ... 原有导航代码,直接使用auxParam ); } } export default withRouter(NavMenu);
2. 转为函数组件,直接使用useParams
如果允许修改NavMenu为函数组件,直接使用React Router的hooks更简洁:
import { useParams } from 'react-router-dom'; import { useState } from 'react'; const NavMenu = () => { const { auxParam } = useParams<{ auxParam: string }>(); const [isOpen, setIsOpen] = useState(false); return ( <header> <Navbar> <Container> <NavbarBrand tag={Link} to="/">My site (React)</NavbarBrand> <ul> <NavItem> <NavLink tag={Link} to={`/${auxParam}/site/foo`}>Extra</NavLink> </NavItem> </ul> </Container> </Navbar> </header> ); }; export default NavMenu;
3. 使用useMatch精准匹配路由(支持函数/类组件)
如果路由结构复杂,useMatch可以精准匹配指定路由并提取参数:
- 函数组件直接使用:
import { useMatch } from 'react-router-dom'; const NavMenu = () => { const match = useMatch('/:auxParam/site/foo/:startIndex?'); const auxParam = match?.params.auxParam || ''; return ( // ... 使用auxParam渲染导航 ); };
- 类组件可通过自定义
withRouter将match传入,参考方案1。
4. 通过路由上下文解析(不推荐,内部API可能变更)
React Router v6提供了内部上下文RouterContext,可直接消费获取路由信息,但注意这是未公开的API,未来可能变动:
import { RouterContext } from 'react-router-dom'; class NavMenu extends React.PureComponent<NavMenuProps, { isOpen: boolean }> { static contextType = RouterContext; context!: React.ContextType<typeof RouterContext>; public render() { const { location } = this.context; // 用正则从pathname中提取参数,比split更可靠 const paramMatch = location.pathname.match(/^\/([^/]+)\/site\/foo/); const auxParam = paramMatch ? paramMatch[1] : ''; return ( // ... 使用auxParam渲染导航 ); } } export default NavMenu;
内容的提问来源于stack exchange,提问作者Gigi
相关产品推荐
相关产品推荐

