|
|
import { useCallback, useEffect, useState, useRef } from 'react';
|
|
|
import { useMatchRoute } from './useMatchRoute';
|
|
|
import { history } from '@umijs/max';
|
|
|
|
|
|
export interface KeepAliveTab {
|
|
|
isHideTab: any;
|
|
|
title: string;
|
|
|
routePath: string;
|
|
|
key: string; // 这个key,后面刷新有用到它
|
|
|
pathname: string;
|
|
|
icon?: any;
|
|
|
children: any;
|
|
|
}
|
|
|
|
|
|
function getKey() {
|
|
|
return new Date().getTime().toString();
|
|
|
}
|
|
|
|
|
|
export function useKeepAliveTabs() {
|
|
|
const [keepAliveTabs, setKeepAliveTabs] = useState<KeepAliveTab[]>([]);
|
|
|
const [activeTabRoutePath, setActiveTabRoutePath] = useState<string>('');
|
|
|
|
|
|
const keepAliveShowEvents = useRef<Record<string, Array<() => void>>>({});
|
|
|
const keepAliveHiddenEvents = useRef<Record<string, Array<() => void>>>({});
|
|
|
|
|
|
const matchRoute = useMatchRoute();
|
|
|
|
|
|
const onShow = useCallback((cb: () => void) => {
|
|
|
if (!keepAliveShowEvents.current[activeTabRoutePath]) {
|
|
|
keepAliveShowEvents.current[activeTabRoutePath] = [];
|
|
|
}
|
|
|
keepAliveShowEvents.current[activeTabRoutePath].push(cb);
|
|
|
}, [activeTabRoutePath])
|
|
|
|
|
|
const onHidden = useCallback((cb: () => void) => {
|
|
|
if (!keepAliveHiddenEvents.current[activeTabRoutePath]) {
|
|
|
keepAliveHiddenEvents.current[activeTabRoutePath] = [];
|
|
|
}
|
|
|
keepAliveHiddenEvents.current[activeTabRoutePath].push(cb);
|
|
|
}, [activeTabRoutePath])
|
|
|
|
|
|
// 关闭tab
|
|
|
const closeTab = useCallback(
|
|
|
(routePath: string = activeTabRoutePath) => {
|
|
|
|
|
|
const index = keepAliveTabs.findIndex(o => o.routePath === routePath);
|
|
|
if (keepAliveTabs[index].routePath === activeTabRoutePath) {
|
|
|
if (index > 0) {
|
|
|
history.push(keepAliveTabs[index - 1].routePath);
|
|
|
} else {
|
|
|
history.push(keepAliveTabs[index + 1].routePath);
|
|
|
}
|
|
|
}
|
|
|
keepAliveTabs.splice(index, 1);
|
|
|
|
|
|
delete keepAliveHiddenEvents.current[routePath];
|
|
|
delete keepAliveShowEvents.current[routePath];
|
|
|
|
|
|
setKeepAliveTabs([...keepAliveTabs]);
|
|
|
},
|
|
|
[activeTabRoutePath],
|
|
|
);
|
|
|
|
|
|
// 关闭其他
|
|
|
const closeOtherTab = useCallback((routePath: string = activeTabRoutePath) => {
|
|
|
const toCloseTabs = keepAliveTabs.filter(o => o.routePath !== routePath);
|
|
|
// 清除被关闭的tab注册的onShow事件和onHidden事件
|
|
|
toCloseTabs.forEach(tab => {
|
|
|
delete keepAliveHiddenEvents.current[tab.routePath];
|
|
|
delete keepAliveShowEvents.current[tab.routePath];
|
|
|
});
|
|
|
|
|
|
setKeepAliveTabs(prev => prev.filter(o => o.routePath === routePath));
|
|
|
}, [activeTabRoutePath]);
|
|
|
|
|
|
// 刷新tab
|
|
|
const refreshTab = useCallback((routePath: string = activeTabRoutePath) => {
|
|
|
setKeepAliveTabs(prev => {
|
|
|
const index = prev.findIndex(tab => tab.routePath === routePath);
|
|
|
|
|
|
if (index >= 0) {
|
|
|
// 这个react的特性,key变了,组件会卸载重新渲染
|
|
|
prev[index].key = getKey();
|
|
|
}
|
|
|
|
|
|
delete keepAliveHiddenEvents.current[prev[index].routePath];
|
|
|
delete keepAliveShowEvents.current[prev[index].routePath];
|
|
|
|
|
|
return [...prev];
|
|
|
});
|
|
|
}, [activeTabRoutePath]);
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
|
if (!matchRoute) return;
|
|
|
|
|
|
const existKeepAliveTab = keepAliveTabs.find(o => o.routePath === matchRoute?.routePath);
|
|
|
|
|
|
setActiveTabRoutePath(matchRoute.routePath);
|
|
|
|
|
|
// 如果不存在则需要插入
|
|
|
if (!existKeepAliveTab) {
|
|
|
setKeepAliveTabs(prev => [...prev, {
|
|
|
title: matchRoute.title,
|
|
|
key: getKey(),
|
|
|
routePath: matchRoute.routePath,
|
|
|
pathname: matchRoute.pathname,
|
|
|
children: matchRoute.children,
|
|
|
icon: matchRoute.icon,
|
|
|
isHideTab: matchRoute.isHideTab
|
|
|
}]);
|
|
|
} else if (existKeepAliveTab.pathname !== matchRoute.pathname) {
|
|
|
// 如果是同一个路由,但是参数不同,我们只需要刷新当前页签并且把pathname设置为新的pathname, children设置为新的children
|
|
|
setKeepAliveTabs(prev => {
|
|
|
const index = prev.findIndex(tab => tab.routePath === matchRoute.routePath);
|
|
|
|
|
|
if (index >= 0) {
|
|
|
prev[index].key = getKey();
|
|
|
prev[index].pathname = matchRoute.pathname;
|
|
|
prev[index].children = matchRoute.children;
|
|
|
}
|
|
|
|
|
|
delete keepAliveHiddenEvents.current[prev[index].routePath];
|
|
|
delete keepAliveShowEvents.current[prev[index].routePath];
|
|
|
|
|
|
return [...prev];
|
|
|
});
|
|
|
} else {
|
|
|
// 如果存在,触发组件的onShow的回调
|
|
|
(keepAliveShowEvents.current[existKeepAliveTab.routePath] || []).forEach(cb => {
|
|
|
cb();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 路由改变,执行上一个tab的onHidden事件
|
|
|
(keepAliveHiddenEvents.current[activeTabRoutePath] || []).forEach(cb => {
|
|
|
cb();
|
|
|
});
|
|
|
|
|
|
}, [matchRoute])
|
|
|
|
|
|
|
|
|
return {
|
|
|
keepAliveTabs,
|
|
|
activeTabRoutePath,
|
|
|
closeTab,
|
|
|
refreshTab,
|
|
|
closeOtherTab,
|
|
|
onShow,
|
|
|
onHidden,
|
|
|
}
|
|
|
}
|