实现思路
左侧菜单和tab页公用同一数据,菜单的激活项和tab项由同一变量控制
建立全局变量
通过useContext,useReducer模拟实现redux
import React, { ReactElement, useContext, useReducer } from 'react';
interface Props {
reducer: any;
initValue: any;
children: any;
}
let a: any;
export const AppContext = React.createContext(a);
export function AppProvider({
reducer,
initValue,
children,
}: Props): ReactElement {
return (
<AppContext.Provider value={useReducer(reducer, initValue)}>
{children}
</AppContext.Provider>
);
}
export const useAppState = (): any => useContext(AppContext);
注入应用中
const App = (): JSX.Element => {
return (
<AppProvider initValue={initState()} reducer={reducer}>
<BrowserRouter>
<Switch>
<Route path="/Manage" component={CoreLayout} />
<Redirect from="/" to="Manage" />
</Switch>
</BrowserRouter>
</AppProvider>
);
};
export const useAppState = (): any => useContext(AppContext);
export default App;
左侧菜单
const { Sider } = Layout;
const { SubMenu } = Menu;
function SideMenu(): JSX.Element {
const [collapsed, setCollapsed] = useState(false);
const [state, dispatch] = useAppState();
const { activeTabKey, menuList } = state;
const handleClickMenuItem = (): void => {
const tabKey = window.location.pathname;
dispatch({
type: MENU_CLICK,
tabKey,
});
};
const renderMenu = (menus: MenuInterface[]): any => {
return menus.map((menu: MenuInterface) => {
if (menu.children && menu.children.length > 0) {
return (
<SubMenu
key={menu.id}
style={{ display: menu.hidden ? 'none' : 'block' }}
title={
<span>
{menu.icon && <Icon type={menu.icon} />}
<span>
<span className="menu-text">{menu.name}</span>
</span>
</span>
}
>
{renderMenu(menu.children)}
</SubMenu>
);
}
return (
<Menu.Item
key={menu.id}
style={{ display: menu.hidden ? 'none' : 'block' }}
onClick={handleClickMenuItem}
>
<Link
to={{
pathname: menu.url,
state: { action: 'RESET' },
}}
>
{menu.icon && <Icon type={menu.icon} />}
<span className="menu-text">{menu.name}</span>
</Link>
</Menu.Item>
);
});
};
return (
<Sider
collapsible
collapsedWidth={80}
breakpoint="sm"
width={200}
collapsed={collapsed}
onCollapse={setCollapsed}
>
<Menu
style={{ overflowY: 'auto' }}
mode="inline"
theme="dark"
inlineIndent={20}
selectedKeys={[activeTabKey]}
>
{renderMenu(menuList)}
</Menu>
</Sider>
);
}
export default SideMenu;
Tab页实现
const { TabPane } = Tabs;
export default function MainLayout(): ReactElement {
const [state, dispatch] = useAppState();
const { tabList, activeTabKey } = state;
const handleTabChange = (activeKey: string): void => {
dispatch({
type: CHANGE_TAB,
tabKey: activeKey,
});
};
const remove = (targetKey: string): void => {
dispatch({
type: DELETE_TAB,
tabKey: targetKey,
});
};
const hanleEdit = (
targetKey: string | React.MouseEvent<HTMLElement>,
action: string
): void => {
if (action === 'remove') {
remove(String(targetKey));
}
};
return (
<div>
<Tabs
hideAdd
onChange={handleTabChange}
activeKey={activeTabKey}
type="editable-card"
onEdit={hanleEdit}
>
{tabList.map((panel: MenuInterface) => (
<TabPane tab={panel.name} key={panel.id}>
{getTabscomponent(panel.id).component}
</TabPane>
))}
</Tabs>
</div>
);
}
多Tab页实现
const Loading = (): JSX.Element => <span>Loading...</span>;
const Production = Loadable({
loader: () => import('../routers/Manage/Producation/Production/Production'),
loading: Loading,
delay: 150,
});
const NotFound = Loadable({
loader: () => import('../routers/404/404'),
loading: Loading,
delay: 150,
});
export interface TabModal {
name: string;
id?: string;
component: JSX.Element;
}
export const getTabscomponent = (key: string): TabModal => {
let newKey = key;
if (key.includes('?')) {
newKey = key.split('?')[0];
}
const tab: TabModal = {
name: '没有找到',
component: <NotFound />,
};
switch (newKey) {
case '/Manage/Production':
tab.name = '页面A';
tab.component = <Production />;
break;
default:
break;
}
return tab;
};
总结
左侧菜单的Link的path和tab页tabKey都是同一路径