报错信息:
Uncaught TypeError: Cannot read properties of null (reading ‘useRef’)
The above error occurred in the component:
at BrowserRouter (http://localhost:3000/static/js/bundle.js:4864:5)
at App Consider adding an error boundary to your tree to customize error handling behavior.
解决方案:
我的环境是:
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.16.0",
"react-router-dom": "^6.16.0",
create-react-app v5
我遇到这个问题时,一开始怀疑语法问题,但最后都排除了。
我的解决方案是:
1.先尝试 清除缓存 执行npm cache clean --force
2.npm start看看行不行
3.如果不行的话,删除node_moudels 然后再cnpm install (我试了几次都可以)
4.最后 npm start就可以了
我还尝试过 npm uninstall react-router-dom 然后再重新下载也不行。
我觉得原因 可能是node_modules下的.store里造成的,我单独删除 .cache 不行,然后删除.store 直接报错。最后删除整个node_modules才行的。
配置路由的步骤:
1.首先 npm react-router-dom
2.App.js 里 写上如下代码:
import React from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import routes from './routes';
const App = () => {
console.log(routes)
return (
<BrowserRouter>
<Routes>
{routes.map((route) => (
<Route key={route.path} path={route.path} element={route.component} />
))}
{/* <Route path="*" element={<Home />} /> */}
</Routes>
</BrowserRouter>
);
};
export default App;
3.index.js里写上:
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
4.在src下新建一个 routes.js文件 写上:
// 导入你的页面组件
import Home from './pages';
const routes = [
{ path: "/", component: <Home/>}
];
export default routes;
运行看到 Home组件里的东西就说明成功了。之后可以在 scr/router.js里导入其他组件 写上 path和routers就行了。
注意:react-router-dom v6 和v5 api不太一样,比如 v5 把 Route 上是 component v6是element。具体写法 建议参考 你所用的版本 文档!
注意:BrowserRouter 是 不带井号的,所以 部署时后端需要 配合一下 比如:重定向到 index.html
具体配置,建议自行查阅。
比如:在Tomcat的web.xml文件中添加以下配置,以将所有请求重定向到应用程序的入口文件上:
<web-app>
<!-- ... -->
<error-page>
<error-code>404</error-code>
<location>/your-app-name/index.html</location>
</error-page>
</web-app>
如果你有需要部署到非根目录建议可以参考:create-react-app v5 打包配置(部署到非根目录)
react-router-dom v6 路由模式
BrowserRouter
优点:
-
使用HTML5的history API,通过修改URL的路径和查询参数来管理路由状态。
-
可以在URL中显示清晰的路由信息,不会使用#符号。
-
支持通过直接输入URL或使用浏览器的前进/后退按钮导航到特定的路由。
缺点:
- 当刷新页面时,向后端服务器发出请求,需要在服务器上进行相应的配置,以确保在前端应用中正确加载页面。
使用方法:
import React from "react";
import { BrowserRouter, Routes, Route , MemoryRouter , HashRouter} from "react-router-dom";
import routes from "./routes.js";
const App = () => {
return (
// basename="/fund_diag_h5"
<BrowserRouter>
<Routes>
{routes.map((route) => (
<Route key={route.path} path={route.path} element={route.component} />
))}
{/* <Route path="/detail/:fundCode" element={<FundDetail />} /> */}
</Routes>
</BrowserRouter>
);
};
export default App;
这个是官方推荐的路由方式,使用最多,不过需要后端配合!
使用场景:
-
需要在URL中显示清晰的路由信息,并且支持直接输入URL或使用浏览器的前进/后退按钮导航到特定路由的场景。
-
需要支持搜索引擎优化(SEO)的应用。
HashRouter
优点:
-
使用哈希路由,通过在URL中添加#符号和哈希值来管理路由状态,可以在前端应用中实现基本的路由功能。
-
可以方便地通过直接输入URL或使用浏览器的前进/后退按钮导航到特定的路由。
缺点:
-
URL中会带有#符号,对一些用户来说可能不太友好。
-
可能会存在一些兼容性问题,某些情况下哈希部分的URL可能会被忽略(比如:微信里就会把#后的东西截取掉)。
使用方法:
import React from "react";
import { BrowserRouter, Routes, Route , MemoryRouter , HashRouter} from "react-router-dom";
import routes from "./routes.js";
const App = () => {
return (
// basename="/fund_diag_h5"
<HashRouter>
<Routes>
{routes.map((route) => (
<Route key={route.path} path={route.path} element={route.component} />
))}
{/* <Route path="/detail/:fundCode" element={<FundDetail />} /> */}
</Routes>
</HashRouter>
);
};
export default App;
这个方式,也挺常见的,因为方便不需要后端配合,但是 致命缺点就是 url里有#号!
使用场景:
-
需要在URL中显示路由信息,并且需要支持直接输入URL或使用浏览器的前进/后退按钮导航到特定路由的场景。
-
对搜索引擎友好性要求不高的一些应用。
MemoryRouter
优点:
-
内存中的路由状态管理,不会反映在URL上,不会导致页面刷新和跳转。
-
适用于不需要在URL中显示路由信息,或在一些特殊场景中使用,如测试或某些客户端应用程序,可以使用内存中的路由状态进行导航和渲染。
缺点:
-
无法通过直接输入URL或通过浏览器前进/后退按钮导航到特定路由。
-
不支持搜索引擎爬取。搜索引擎无法通过内存中的路由状态获得页面内容。
使用方法:
import React from "react";
import { BrowserRouter, Routes, Route , MemoryRouter , HashRouter} from "react-router-dom";
import routes from "./routes.js";
const App = () => {
return (
// basename="/fund_diag_h5"
<MemoryRouter>
<Routes>
{routes.map((route) => (
<Route key={route.path} path={route.path} element={route.component} />
))}
{/* <Route path="/detail/:fundCode" element={<FundDetail />} /> */}
</Routes>
</MemoryRouter>
);
};
export default App;
这个一般很少使用。
怎么理解:内存中的路由状态管理,不会反映在URL上,不会导致页面刷新和跳转?
我尝试了一下:
就是如果你路由传参,参数不会显示到url地址栏里:
比如: navigate(/detail?fundCode=${data.fundCode}
) 跳转到详情页但是 后面的?fundCode被忽略了
而且浏览器 后退,前进 失效了。
navigate(`/detail?fundCode=${data.fundCode}`)
如果部署到非根目录,则不用家basename 也可以正常显示。
适用场景:
-
不需要在URL中显示路由信息,或在一些特殊的客户端应用程序中,可以使用内存中的路由状态进行导航和渲染。
-
单元测试等需要在不刷新页面的情况下进行路由状态管理的场景。
实现动态 title
react-router v6实现动态的title(react-router-dom v6)
总结:
根据具体的需求和应用场景,选择合适的路由组件来管理你的应用程序的导航和页面渲染。如果不需要在URL中显示路由信息,则可以使用MemoryRouter;如果需要使用哈希路由,并且不介意URL中的#符号,则可以使用HashRouter;如果需要在URL中显示清晰的路由信息,并且支持直接输入URL或使用浏览器的前进/后退按钮导航到特定路由,则可以使用BrowserRouter。
路由组件 | 内存历史记录(MemoryRouter) | 哈希路由(HashRouter) | 浏览器历史记录(BrowserRouter) |
---|---|---|---|
功能 | 在内存中管理路由状态,不会反映在URL上,不会导致页面刷新和跳转。 | 使用URL中的哈希路由,通过添加#符号和哈希值来管理路由状态。 | 使用HTML5的history API,通过修改URL的路径和查询参数来管理路由状态。 |
URL显示 | 不显示 | 显示,带有#符号 | 显示,清晰的URL路径和查询参数。 |
页面刷新 | 无法刷新页面,不会向服务器发送请求 | 可以刷新页面,但仍在前端应用中加载,不会向服务器发送请求 | 可以刷新页面,在刷新时向服务器发送请求 |
导航和跳转 | 无法通过直接输入URL或浏览器前进/后退按钮导航到特定路由,适用于内存导航或特殊场景。 | 可以通过直接输入URL或浏览器前进/后退按钮导航到特定路由,适用于基本路由需求。 | 可以通过直接输入URL或浏览器前进/后退按钮导航到特定路由,适用于清晰路由需求和SEO优化。 |
搜索引擎优化 | 不支持 | 不支持 | 支持 |
适用场景 | - 不需要在URL中显示路由信息 - 测试目的 - 特殊客户端应用程序 | 需要在URL中显示路由信息 - 不要求搜索引擎优化的前端应用程序 | - 需要在URL中显示清晰的路由信息 - 需要搜索引擎优化的应用程序 |
请注意,上述表格是根据对每种路由组件的特性进行的总结,并非完整的列表。你在选择适合你应用程序的路由组件时,还要考虑其他因素,例如性能、需求复杂度和开发团队的熟悉程度等。