我在浏览器应用中使用react-router-dom和react-router-config v4。我需要从路线图中获得多级选项卡菜单(每条路线要么有一个要渲染的组件,要么子路径要显示为下一级菜单)。
我想获得以下菜单结构:
从这样的解释:
const routes = [
{
path: "/Tab1",
name: "Tab 01"
component: Tab1
},
{
path: "/Tab2",
name: "Tab 02"
component: Tab12
},
{
path: "/Tab3",
name: "Tab 03"
component: null,
routes: [
{
path: "/Tab3/SubTab1",
name: "SubTab 01"
component: SubTab1
},
{
path: "/Tab3/SubTab2",
name: "SubTab 02"
component: SubTab2
},
{
path: "/Tab3/SubTab3",
name: "SubTab 03"
component: null,
routes: [
...
]
},
]
},
...
];
答案 0 :(得分:2)
我提出了一个令人满意的解决方案(在TypeScript片段之后很长,但大多数是路由图定义)。
import * as React from "react";
import * as ReactDOM from "react-dom";
import { BrowserRouter, Route, Link, } from "react-router-dom";
import { renderRoutes, RouteConfig, MatchedRoute } from 'react-router-config'
import { Location } from "history";
declare module 'react-router-config'{
interface RouteConfig{
tabName?: string;
defaultSubpath?: string;
}
interface MatchedRoute<T>{
location: Location;
}
}
// todo optimize with memoization?
function getActiveRoutes(match: MatchedRoute<any>):RouteConfig[]{
const currentPath = match.location.pathname;
const routes = match.route.routes;
let activeRoutes:RouteConfig[] = [];
fillActiveRoutes(routes);
return activeRoutes;
function fillActiveRoutes(current: RouteConfig[]){
for(const route of current){
activeRoutes.push(route);
let isActive = false;
if(!route.routes || route.routes.length === 0){
isActive = route.path === currentPath;
} else if(route.routes) {
let isActive = fillActiveRoutes(route.routes);
}
if(isActive === false){
activeRoutes.pop();
} else {
break;
}
}
}
}
const ChildLinks = (match: MatchedRoute<any>) => {
let activeRoutes = getActiveRoutes(match);
return(<div>
{match.route.routes.map((route) => {
let isActive = activeRoutes.some(x => x === route);
let to = route.defaultSubpath || route.path;
let key = 'main-tabs-link-' + route.path;
let label = isActive ? ` [${route.tabName}] ` : ` ${route.tabName} `;
return (<Link to={to} key={key}> {label} </Link>);
})
}
</div>);
}
const EmptyRenderer:React.StatelessComponent<MatchedRoute<any>> = (match: MatchedRoute<any>) => (<div>
{ChildLinks(match)}
{renderRoutes(match.route.routes)}
</div>);
const Root:React.StatelessComponent<MatchedRoute<any>> = (match: MatchedRoute<any>) => (<div>
<h1>Root</h1>
{ EmptyRenderer(match) }
</div>);
const StaticDiv: (content:string) => React.StatelessComponent<MatchedRoute<any>> = (content:string) =>
() => (<div>{content}</div>)
const routes:RouteConfig[] = [
{ component: Root,
routes: [
{ path: '/A/',
tabName: 'A',
exact: true,
component: StaticDiv("A")
},
{ path: '/B/',
tabName: 'B',
defaultSubpath: '/B/2/',
exact: false,
component: EmptyRenderer,
routes: [
{
path: '/B/1/',
exact: true,
tabName: "B1",
component: StaticDiv("B1")
},{
path: '/B/2/',
exact: true,
tabName: "B2",
component: StaticDiv("B2")
},{
path: '/B/3/',
exact: true,
tabName: "B3",
component: StaticDiv("B3")
}]
},
{ path: '/C/',
tabName: 'C',
defaultSubpath: '/C/3/Z/',
exact: false,
component: EmptyRenderer,
routes: [
{
path: '/C/1/',
exact: true,
tabName: "C1",
component: StaticDiv("C1")
},{
path: '/C/2/',
exact: true,
tabName: "C2",
component: StaticDiv("C2")
},{
path: '/C/3/',
defaultSubpath: '/C/3/Z/',
exact: false,
tabName: "C3",
component:EmptyRenderer,
routes: [
{
path: '/C/3/X/',
exact: true,
tabName: "C3X",
component: StaticDiv("C3X")
},{
path: '/C/3/Y/',
exact: true,
tabName: "C3Y",
component: StaticDiv("C3Y")
},{
path: '/C/3/Z/',
exact: true,
tabName: "C3Z",
component: StaticDiv("C3Z")
}]
}]
}
]
}
]
export const Example = () => (<BrowserRouter>
{renderRoutes(routes)}
</BrowserRouter>);
使用的Lib版本:
"dependencies": {
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router": "^4.2.0",
"react-router-config": "^1.0.0-beta.4",
"react-router-dom": "^4.2.2"
},
"devDependencies": {
"@types/react": "^16.0.38",
"@types/react-dom": "^16.0.4",
"@types/react-router-config": "^1.0.6",
"typescript": "^2.7.1"
}