自定义NPM包中用useSearchParams为何仍报useLocation需在Router上下文?
我搜了好几个小时都没找到靠谱的解决方案,干脆自己折腾出结果,写下来帮遇到同样坑的人省时间。
问题描述
我有个用react-router-dom做路由的React项目,同时维护了一个独立的NPM共享包,里面放了多个项目共用的逻辑。我在共享包里写了个自定义Hook,用来监听URL搜索参数变化并更新状态:
// 这段代码在我的共享NPM包中 import { useSearchParams } from 'react-router-dom'; import { useEffect } from 'react'; export function useCustomHookExample(): void { const [searchParams] = useSearchParams(); useEffect(() => { // 这里处理搜索参数逻辑... }, [searchParams]); }
但在主项目里用这个Hook时,直接抛出错误:
Uncaught Error: useLocation() may be used only in the context of a <Router> component.
我当然知道useSearchParams内部调用了useLocation,必须在Router上下文里用,但我明明已经在项目根组件外层套了BrowserRouter:
export function AppRouter() { return ( <BrowserRouter> <Routes> <Route path='/' element={<App />} /> </Routes> </BrowserRouter> ); } export function App() { // 明明在Router上下文里,还是报错😭 useCustomHookExample(); // ...其他代码 }
根本原因
问题出在依赖重复安装上:共享包和主项目各自安装了一份react-router-dom,哪怕版本号看起来差不多,它们也是两个完全独立的模块。主项目的BrowserRouter是主项目里的react-router-dom提供的上下文,而共享包里的useSearchParams用的是共享包自己的react-router-dom的上下文,两者不互通,所以Hook找不到对应的Router。
解决方案
统一react-router-dom版本
确保主项目和共享包使用的react-router-dom版本完全一致,比如都用^6.14.0。把react-router-dom设为peer依赖
在共享包的package.json中,把react-router-dom从dependencies或devDependencies移到peerDependencies,这样共享包会直接使用主项目安装的react-router-dom,不会自己单独装一份:
{ "peerDependencies": { "react-router-dom": "^6.0.0" } }
同时可以加上peerDependenciesMeta让安装更友好:
{ "peerDependenciesMeta": { "react-router-dom": { "optional": false } } }
- 强制主项目依赖统一(可选)
如果是用npm,可以在主项目的package.json里加overrides强制所有依赖的react-router-dom版本统一:
{ "overrides": { "react-router-dom": "^6.14.0" } }
yarn用户可以用resolutions,pnpm用户用pnpm.overrides。
- 清除缓存并重启服务
删掉主项目和共享包的node_modules,重新安装依赖,然后重启开发服务器,确保修改生效。
验证方式
在主项目里打印两个模块的引用是否一致,确认问题解决:
import { useSearchParams as appUseSearchParams } from 'react-router-dom'; import { useSearchParams as pkgUseSearchParams } from '你的共享包名称'; console.log(appUseSearchParams === pkgUseSearchParams); // 输出true说明依赖统一了
内容的提问来源于stack exchange,提问作者souyahia

