Using tabs navigation in background projects is an important function
The following describes how to implement this function in conjunction with the UMI framework and the corresponding plug-ins
Reference: react activation
Reference implementation: implement a tabs
At present, the architecture design of the project is umi3 + react17 + antd pro5
1. Introduce and use relevant plug-ins
Plug in address: UMI plugin keep alive
npm install umi-plugin-keep-alive --save
# or
yarn add umi-plugin-keep-alive
2. Common component packaging
Create keepavlietabs under the components folder
Please modify your own requirements for the corresponding less style
For relevant keepalive and life cycle usage schemes and problems, please go to the above link react activation
Create index Tsx file
// /components/KeepAvlieTabs/index.tsx
import { useAliveController } from 'react-activation';
import { arrayDeduplicate } from '@/utils/Array';
import type { CachingNode } from './type';
import Tab from './Tab';
import styles from './index.less';
import { useHistory, useLocation } from 'umi';
export default function KeepAliveTabs() {
//History navigation
const history = useHistory();
//Local routing information
const location = useLocation();
//Get cache node method and information
const { getCachingNodes } = useAliveController();
const cachingNodes: CachingNode[] = getCachingNodes();
//Because it is an asynchronous component, you need to deal with the duplication in the cache here
let nodes: CachingNode[] = arrayDeduplicate(cachingNodes, 'path');
//The home page does not participate in the closing operation of tabs switching
nodes = nodes.filter((item) => item.path !== '/home');
return (
<ul className={styles['alive-tabs']}>
<li
className={location.pathname === '/home' ? styles.home_active : styles.home_deactive}
onClick={() => {
history.push('/home');
}}
>
<div className="tags-nav">
<span>Front page</span>
</div>
</li>
{nodes.map((node) => (
<Tab key={node!.id} node={node} />
))}
</ul>
);
}
Create tab Tsx file
// /components/KeepAvlieTabs/Tab.tsx
import { useHistory, useLocation } from 'umi';
import { useAliveController } from 'react-activation';
import { CloseCircleOutlined } from '@ant-design/icons';
import type { CachingNode } from './type';
import styles from './index.less';
export default function Tab({ node }: { node: CachingNode }) {
const history = useHistory();
const location = useLocation();
//The same as above. Dropscope is to release the node. Click Delete to delete the current node information
const { getCachingNodes, dropScope } = useAliveController();
const cachingNodes: CachingNode[] | any[] = getCachingNodes();
//Delete tab
function dropTab(e: React.MouseEvent<HTMLButtonElement>) {
e.stopPropagation();
//If you close the keepalive tab in activation, you need to leave the current route first
//Drop after keepalive deactivated is triggered
if (location.pathname === node.path) {
//Route asynchronous load control
const unlisten = history.listen(() => {
setTimeout(() => {
dropScope(node.name as string | RegExp);
}, 30);
});
unlisten();
//Go to the last tab after excluding the current node
if (cachingNodes.length <= 1) {
history.push('/');
} else {
const { path } = cachingNodes.filter((item) => item.path !== node.path).pop();
history.push(path);
}
} else {
dropScope(node.name as string | RegExp);
}
}
//Set the style of the current tab
const className = () => {
if (location.pathname === node.path) {
if (location.pathname === '/home') {
return `${styles.active} ${styles.home_active}`;
}
return `${styles.active}`;
}
return `${styles.deactive}`;
};
return (
<li
className={className()}
onClick={() => {
history.push(node.path);
}}
>
<div className="tags-nav">
<span>{node.name}</span>
{<CloseCircleOutlined className={styles['close-btn']} onClick={dropTab} />}
</div>
</li>
);
}
Create type file type ts
export type CachingNode = {
createTime: number;
updateTime: number;
name?: string;
id: string;
[key: string]: any;
};
Create style less file
.alive-tabs {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
white-space: nowrap;
background: #fff;
li {
position: relative;
display: inline-block;
padding: 0 28px 0 10px;
line-height: 32px;
vertical-align: top;
list-style: none;
border-right: solid 1px #e6e6e6;
cursor: pointer;
transition: background 0.2s;
}
.active {
height: 32px;
background: rgba(#1890ff, 0.1);
border-bottom: 2px solid #1890ff;
// background-color: #42b983;
}
.home_active {
height: 32px;
padding: 0 10px;
background: rgba(#1890ff, 0.1);
border-bottom: 2px solid #1890ff;
// background-color: #42b983;
}
.home_deactive {
padding: 0 10px;
}
.deactive {
background: #fff;
}
.close-btn {
position: absolute;
top: 11px;
right: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
line-height: 0;
outline: none;
// transform: translateY(-50%);
}
}
3. Use keepalive in the component
Create books / index tsx
import React from 'react';
import { KeepAlive, useActivate, useUnactivate } from 'react-activation';
const Books: React.FC = () => {
useActivate(()=>{
console. Log ("I am activated");
})
useUnactivate(()=>{
console. Log ("I'm cached");
})
Return < div > I am a cached component < / div >;
};
export default (props: any) => {
//The following method is used to control the routing operation
//This is to synchronously display the title in the route definition and the matching route path
//You can modify the current logic according to your own needs
return (
<KeepAlive name={props.route.name} path={props.route.path} saveScrollPosition="screen">
<Books />
</KeepAlive>
);
};
4. Mount keepalivetabs to the interface
Our page layout has a top left and bottom structure in app In TSX, it is attached to our header, beyond the display of the scroll bar. Please add specific styles and functions by yourself
The right mouse button menu is not implemented. All are closed. Switch left and right. Please add it yourself. Antd has related components
export const layout: RunTimeLayoutConfig = ({ initialState }: any) => {
return {
//Controls whether components can be cached
disableMobile: true,
headerContentRender: () => <KeepAliveTabs />,
...
};
};