问题描述
我正在实现日志跳转的任务,因为有多个模块用到了日志,需要实现各个日志的互相跳转。
列表页
详情页
目前结构大概描述一下,比如说,任务式建模和notebook里面会有列表页,每个列表里面都会有很多任务,任务点进去后会有一个Tab页是日志页面
然后问题出现了:
- 当我从 「任务式建模」里面某个任务的日志页面,跳转到「Notebook」里面某个任务的日志页面的时候,一切正常。
- 而当我从「任务式建模」里面某个任务的日志界面,跳到「任务式建模」里面另外一个任务的日志界面时,点击了后发现url发生了改变,但是页面却没有发生变化。自己手动刷新后会跳转到正确的页面(但是这样子肯定不行)
在url上面的变化可以看这张图
解决过程/思路
百度上尝试了很多别人发的方法,包括但不限于
- 禁用严格模式—我这边本身没有开严格模式——失败
- 将路由组件的删除,只在App组件包裹 —— 用的是公司包装过的路由组件,且确实也只包了一层。失败
- 尝试先跳转到一个白屏页面(我尝试的是跳转到列表页),然后再跳转回来——可以实现,但是很不优雅,而且这样子的话如果同时使用浏览器右上角的前进后退,会导致出现很混乱的后果
- 使用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