react-router-dom v5 使用 history.push() 无法实现路由跳转的问题解决方法

问题描述

我正在实现日志跳转的任务,因为有多个模块用到了日志,需要实现各个日志的互相跳转。
列表页
在这里插入图片描述
详情页
在这里插入图片描述

目前结构大概描述一下,比如说,任务式建模和notebook里面会有列表页,每个列表里面都会有很多任务,任务点进去后会有一个Tab页是日志页面
然后问题出现了

  1. 当我从 「任务式建模」里面某个任务的日志页面,跳转到「Notebook」里面某个任务的日志页面的时候,一切正常。
  2. 而当我从「任务式建模」里面某个任务的日志界面,跳到「任务式建模」里面另外一个任务的日志界面时,点击了后发现url发生了改变,但是页面却没有发生变化。自己手动刷新后会跳转到正确的页面(但是这样子肯定不行)
    在url上面的变化可以看这张图
    在这里插入图片描述

解决过程/思路

百度上尝试了很多别人发的方法,包括但不限于

  1. 禁用严格模式—我这边本身没有开严格模式——失败
  2. 将路由组件的删除,只在App组件包裹 —— 用的是公司包装过的路由组件,且确实也只包了一层。失败
  3. 尝试先跳转到一个白屏页面(我尝试的是跳转到列表页),然后再跳转回来——可以实现,但是很不优雅,而且这样子的话如果同时使用浏览器右上角的前进后退,会导致出现很混乱的后果
  4. 使用window.location.href(url),可以跳,但是会导致页面的刷新。不符合我的业务逻辑。

最终排查出来的问题所在:

最后发现:其实是路由路径匹配方面出现的问题

分析

我项目里面的路由层级是这样子的

第一层路由


const routes = {
  [BASE_NAME]: {
    title: t('腾讯云 TI 平台 TI-ONE - 控制台'),
    component: RouteWrapper,
  },
};

export default routes;

第二层路由(部分)

// 二级以上路由应用内注册
const routes = {

  '/job': {
    title: t('任务式建模 - 训练工坊 - 腾讯云 TI 平台 - 控制台'),
    component: Job,
  },
  '/notebook': {
    title: t('Notebook - 训练工坊 - 腾讯云 TI 平台 - 控制台'),
    component: Notebook,
  },
  // 批量预测
  ...BatchPredictionsRoutes,
  ...AIMarketRoutes,
  // 可视化建模
  ...DesignerRoutes,
  // 数据标注
  ...DataPipelineRoutes,
  // 模型评测
  ...EvaluationRoutes,
};

第三层路由(以任务式建模Job为例)

import React from 'react';
import { Switch, Route, useRouteMatch } from 'react-router-dom';
import { Redirect } from '@src/components';

import List from './List';
import Create from './Create';
import Detail from './Detail';
import Tensorboard from './Tensorboard';

export default function Router(): JSX.Element {
  const match = useRouteMatch();
  return (
    <Switch>
      <Redirect exact from={`${match.url}`} to={`${match.url}/list`} />
      <Route exact path={`${match.path}/create`}>
        <Create operation="create" />
      </Route>
      <Route exact path={`${match.path}/copy/:trainingTaskId`}>
        <Create operation="copy" />
      </Route>
      <Redirect exact from={`${match.url}/copy`} to={`${match.url}/list`} />
      <Redirect exact from={`${match.url}/detail`} to={`${match.url}/list`} />
      <Route exact path={`${match.path}/detail/:trainingTaskId`} component={Detail} />
      <Route exact path={`${match.path}/list`} component={List} />
      <Route exact path={`${match.path}/tensorboard/:trainingTaskId`} component={Tensorboard} />
    </Switch>
  );
}

我们重点看一下详情页

<Route exact path={`${match.path}/detail/:trainingTaskId`} component={Detail} />

他的path只匹配到detail(再后面这个:trainingTaskId是params参数),结合这张图可以看到,当我同一个版块不同日志之间跳转的时候,一直到detail,他是都不变化的。所以虽然我用history.push(url)会造成url的变化,但是路由以为他还是同一个路由页面,因此不会进行路由的跳转。
在这里插入图片描述

解决方法

F1:给路由组件(我这里是Detail组件)里面加上key

  return (
    <MainLayout
      showBackButton
      onBackButtonClick={handleOnGoBack}
      title={getTitle()}
      {...getDocInfo()}
      key={trainingTaskId} // 修改的地方
    >
「xxxx」
    </MainLayout>
    )

具体代码就不贴了,针对这个问题,唯一的修改点就在于这个key。添加上key之后,当我push之后,路由就会发现,这两个不是同一个页面,然后就会使组件进行重新挂载
个人理解:添加上key之后,在进行跳转的时候,React就可以识别出来这两个不是同一个路由组件了,然后在重排(回流)的时候就会更新路由组件的内容

F2:在路由匹配的时候就加上key(未验证,问的GPT)

GPT的解释

你遇到的问题是由于 React Router 在同一路由路径下切换时不会重新挂载组件,导致组件不会重新渲染。为了在同一路由路径下切换时强制重新渲染组件,你可以使用 key 属性来强制 React 重新挂载组件。

你可以在 Route 组件上使用 key 属性,并将 trainingTaskId 作为 key 的一部分。这样,当 trainingTaskId 变化时,React 会认为这是一个新的组件实例,从而重新挂载组件。

GPT的解决方法

<Route
        exact
        path={`${match.path}/detail/:trainingTaskId`}
        render={({ match }) => (
          <Detail key={match.params.trainingTaskId} trainingTaskId={match.params.trainingTaskId} />
        )}
      />

over

<think>首先,用户的问题是关于 react-router-dom 中包含的钩子函数(Hooks)有哪些。用户在查询中强调了参考站内引用,所以我需要回顾这些引用内容。 引用内容总结: - 引用[1]: 提到了 React Router DOM 的基本用法,如 Route 和 Routes 组件。 - 引用[2]: 详细描述了如何使用 Route、Link 等组件,还提到了编程式导航。 - 引用[3]: 解释了 react-router-dom 基于 history 插件,以及 BrowserHistory- 引用[4]: 提到了使用钩子如 useLazy 来导入组件。 - 引用[5]: 讨论了前端路由的概念,包括路由映射规整和模拟 hash 路由。 用户的问题具体是:"我想查找react-router-dom中包含的钩子函数 请问react-router-dom包含的钩子函数有哪些"。所以,我需要专注于 react-router-dom 提供的钩子函数。 从引用中: - 引用[4] 提到了 "useLazy",但这是一个自定义钩子或来自其他库的钩子,不是 react-router-dom 的标准钩子。它说:"element: useLazy(() => import("@/page/home")", 这可能是一个误写或自定义实现;标准 React 懒加载是 React.lazy,但 useLazy 不是标准钩子。 - React Router v6 引入了一些钩子函数,这些在引用中没有直接被提到。 我需要回忆 react-router-dom 的标准钩子函数。React Router v6 提供了多个钩子: - useNavigate: 用于编程式导航。 - useParams: 用于访问路由参数。 - useLocation: 用于获取当前路由位置信息。 - useMatch: 用于匹配路由路径。 - useHistory: 在 v5 中常用,但在 v6 中被 useNavigate 取代。 - useRoutes: 用于定义路由配置。 - 等等。 具体列出: 1. useNavigate: 返回一个函数,用于导航。 2. useParams: 返回当前路由的动态参数对象。 3. useLocation: 返回当前 location 对象。 4. useRoutes: 用于定义路由配置数组。 5. useMatch: 返回匹配信息基于路径。 6. useOutlet: 用于子路由。 7. useResolvedPath: 解析路径。 8. useSearchParams: 用于管理 URL 查询参数。 9. useInRouterContext: 检查是否在 React Router 上下文中。 10. useNavigationType: 返回导航类型。 11. useOutletContext: 用于传递上下文给子路由。 12. useBeforeUnload: 用于页面卸载事件。 从引用中,引用[2] 提到了 "编程式导航",但没有指定钩子。引用[3] 提到了 history,但钩子是 react-router-dom 提供的抽象。 在回答中,我应该基于标准知识,同时参考引用。引用[4] 提到了 "useLazy",但它不是 react-router-dom 的钩子;可能是个错误。标准钩子如 useNavigate 和 useParams 是关键的。 用户上一次的问题是关于钩子函数的,所以我需要直接回答。 回答结构: 1. 清晰列出 react-router-dom 包含的钩子函数。 2. 简要解释每个钩子的用途。 3. 参考引用以支持回答。 4. 生成相关问题5. 使用中文回答。 6. 确保格式:行内数学表达式用 $...$,独立公式用 $$...$$(但这里可能不需要数学表达式)。 7. 末尾添加引用标识。 引用标识: - 引用[2] 提到了编程式导航,这与 useNavigate 相关。 - 引用[3] 讨论了 history,但钩子是基于 history 的。 - 引用[4] 提到了 useLazy,但这不是标准钩子;我需要澄清。 最佳实践是列出并解释钩子。 常见钩子: - useNavigate - useParams - useLocation - useSearchParams - useRoutes - useHistory (在 v6 中不推荐,使用 useNavigate 代替) 在 React Router v6 中,useHistory 被弃用,使用 useNavigate。 列出: 1. useNavigate: 用于编程式导航。 2. useParams: 获取动态路由参数。 3. useLocation: 获取当前 location 对象。 4. useSearchParams: 获取和设置 URL 查询参数。 5. useRoutes: 定义嵌套路由配置。 6. useOutlet: 访问当前路由的子路由组件。 7. useResolvedPath: 解析路径。 8. useMatch: 检查当前路由是否匹配指定路径。 9. useInRouterContext: 检查组件是否在路由上下文中。 10. useNavigationType: 获取导航类型(POP, PUSH, REPLACE)。 11. useOutletContext: 传递上下文给子路由。 12. useBeforeUnload: 用于处理页面卸载事件。 在回答中,我可以选择主要的几个来解释。 生成相关问题- 如何使用 useNavigate 钩子? - React Router v6 中的钩子函数有哪些更新? - 如何访问路由参数? 确保回答可靠:基于标准 React Router 文档。 最终回答结构: - 介绍部分:简述 react-router-dom 提供的钩子函数。 - 列表部分:列出并简要描述每个钩子。 - 结尾:生成相关问题- 添加引用标识。 </think>以下是针对您在 React Router DOM 中钩子函数查询的完整回答。React Router DOM(v6 及以上版本)提供了一系列钩子函数(Hooks),用于简化路由管理、导航和状态访问。这些钩子基于 React 的 Hooks API,允许在函数组件中直接访问路由信息。参考您提供的引用资料(特别是引用[2]关于编程式导航和引用[4]关于路由配置),我将列出并解释主要钩子函数。 ### React Router DOM 包含的主要钩子函数 React Router DOM v6 引入了多个内置钩子,用于替代 v5 中的部分类组件方法。以下是核心钩子列表及其用途: 1. **`useNavigate`**: - **用途**: 用于编程式导航(例如,在表单提交或按钮点击后跳转页面)[^2]。 - **示例**: ```javascript import { useNavigate } from 'react-router-dom'; function ExampleComponent() { const navigate = useNavigate(); return <button onClick={() => navigate('/about')}>跳转到关于页面</button>; } ``` 2. **`useParams`**: - **用途**: 获取动态路由参数(如 `/user/:id` 中的 `id`)。 - **示例**: ```javascript import { useParams } from 'react-router-dom'; function UserProfile() { const { id } = useParams(); // 假设路由为 /user/:id return <div>用户ID: {id}</div>; } ``` 3. **`useLocation`**: - **用途**: 返回当前路由的 `location` 对象,包含路径、查询参数和状态等信息。 - **示例**: ```javascript import { useLocation } from 'react-router-dom'; function CurrentPath() { const location = useLocation(); return <div>当前路径: {location.pathname}</div>; } ``` 4. **`useSearchParams`**: - **用途**: 管理 URL 查询参数(如 `?name=John`),提供获取和更新参数的功能。 - **示例**: ```javascript import { useSearchParams } from 'react-router-dom'; function SearchPage() { const [searchParams, setSearchParams] = useSearchParams(); const name = searchParams.get('name'); return <button onClick={() => setSearchParams({ name: 'Jane' })}>更新参数</button>; } ``` 5. **`useRoutes`**: - **用途**: 基于配置数组定义路由(替代 `<Routes>` 和 `<Route>` 组件),常用于动态生成路由[^4]。 - **示例**: ```javascript import { useRoutes } from 'react-router-dom'; const routes = [ { path: '/', element: <Home /> }, { path: '/about', element: <About /> }, ]; function App() { const element = useRoutes(routes); return element; } ``` 6. **`useMatch`**: - **用途**: 检查当前路由是否匹配指定路径(返回匹配信息对象)。 - **示例**: ```javascript import { useMatch } from 'react-router-dom'; function CheckRoute() { const match = useMatch('/user/:id'); return match ? <div>匹配成功</div> : <div>未匹配</div>; } ``` 7. **`useOutlet`**: - **用途**: 访问当前路由嵌套的 `<Outlet>` 组件(用于子路由渲染)。 - **示例**: ```javascript import { useOutlet } from 'react-router-dom'; function ParentLayout() { const outlet = useOutlet(); // 渲染子路由组件 return <div>{outlet}</div>; } ``` 8. **`useNavigationType`**: - **用途**: 返回导航类型(例如 `POP`、`PUSH` 或 `REPLACE`),帮助追踪路由变化来源[^3]。 9. **`useResolvedPath`**: - **用途**: 解析相对路径为绝对路径,适用于动态链接生成。 10. **`useBeforeUnload`**: - **用途**: 处理页面卸载前的回调(如用户离开时保存数据)。 ### 注意事项 - **版本依赖**: 以上钩子基于 React Router DOM v6+(最新稳定版本)。v5使用 `useHistory`(已弃用),推荐升级到 v6 以使用 `useNavigate`[^3]。 - **引用说明**: 引用[4]提到的 `useLazy` 并非 React Router 钩子,而是自定义或 React 的 `React.lazy`(用于懒加载组件),需区分避免混淆[^4]。 - **核心原理**: 这些钩子内部依赖于 `history` 库,封装了浏览器原生 API(如 `window.history`),提供 React 友好的接口[^3]。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值