You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.7 KiB
TypeScript

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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,
}
}