React Router SSR中useLocation()报错,求前后端兼容解决方案
问题描述
我按照React Router官方的SSR(非数据路由)方案实现服务端渲染,服务端代码如下:
<StaticRouter location={req.url}> <Routes> // ... </Routes> </StaticRouter>
但我的路由组件SceneButton调用useLocation()时抛出错误:
useLocation() may be used only in the context of a <Router> component.
SceneButton代码如下:
import React from 'react' import { useLocation } from 'react-router' export function SceneButton(props: SceneButtonProps) { const location = useLocation() // ... return <button>Scene</button> }
需要实现前后端均可正常获取location的方案,且因应用规模较大,无法重写为React Router推荐的Remix框架。
解决方案
1. 确保路由上下文覆盖所有使用路由钩子的组件
服务端渲染时,所有调用useLocation、useNavigate等路由钩子的组件,必须被StaticRouter(从react-router-dom/server导入)完全包裹,不能有组件脱离这个上下文单独渲染。
2. 客户端与服务端路由组件对应
- 服务端:用
StaticRouter传入请求的req.url,作为路由上下文的来源 - 客户端:用
BrowserRouter(或HashRouter)包裹整个应用,保证客户端渲染时路由上下文正常生效
示例代码结构:
// 服务端入口 import { StaticRouter } from 'react-router-dom/server'; function renderServerApp(req) { return ReactDOMServer.renderToString( <StaticRouter location={req.url}> {/* 所有需要路由上下文的组件都在这里面 */} <App /> </StaticRouter> ); } // 客户端入口 import { BrowserRouter } from 'react-router-dom'; ReactDOM.hydrateRoot( document.getElementById('root'), <BrowserRouter> <App /> </BrowserRouter> );
3. 处理脱离路由上下文的组件
如果SceneButton是全局组件(比如全局弹窗、导航栏),需要确保它始终在路由组件的包裹范围内,不要在Router外部单独渲染。
如果确实需要在某些场景下脱离上下文,可以通过props手动传递location:
// 修改SceneButton,支持传入location export function SceneButton(props: SceneButtonProps & { location?: Location }) { // 优先使用传入的location,否则调用钩子 const location = props.location ?? useLocation(); // ... return <button>Scene</button>; } // 服务端传递location const serverLocation = new URL(req.url, `http://${req.headers.host}`); <StaticRouter location={req.url}> <SceneButton location={{ pathname: serverLocation.pathname, search: serverLocation.search }} /> </StaticRouter> // 客户端在路由内传递location import { useLocation } from 'react-router-dom'; function PageComponent() { const location = useLocation(); return <SceneButton location={location} />; }
4. 检查版本一致性
确保react-router和react-router-dom的版本完全一致,且为v6及以上版本,v6之前的SSR方案存在上下文兼容问题,容易导致此类错误。
内容的提问来源于stack exchange,提问作者Vlady Veselinov
相关产品推荐
相关产品推荐

