Skip to content

Commit 914d12b

Browse files
committed
✨ feat(ActivityKeepAlive & tabStore): add support for reloadKey to force component reloads, enhancing tab management and component lifecycle handling; update component keys to incorporate reloadKey for improved rendering behavior.
1 parent 593095a commit 914d12b

2 files changed

Lines changed: 61 additions & 36 deletions

File tree

src/components/KeepAlive/ActivityKeepAlive.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface CacheItem {
2121
* 3. 优化滚动恢复 - 使用更简洁的方式
2222
* 4. 使用 useMemo 缓存组件列表,减少渲染
2323
* 5. 移除不必要的状态,直接渲染缓存的组件
24+
* 6. 支持 reloadKey 强制重新加载组件
2425
*
2526
* @param children - 子组件
2627
*/
@@ -123,15 +124,23 @@ const ActivityKeepAlive: React.FC<ActivityKeepAliveProps> = memo(({ children })
123124
};
124125
}, []);
125126

127+
// 获取当前 tab 的 reloadKey(用于强制重新加载)
128+
const currentTab = tabs.find((tab) => tab.key === activeKey);
129+
const reloadKey = currentTab?.reloadKey;
130+
126131
// 使用 useMemo 缓存渲染的组件列表
127132
const cachedComponents = useMemo(() => {
128133
const components: React.ReactNode[] = [];
129134

130135
// 渲染所有缓存的组件,使用 Activity 控制显示/隐藏
131136
cacheRef.current.forEach((cache, key) => {
132137
const isVisible = key === activeKey;
138+
const tab = tabs.find((t) => t.key === key);
139+
// 使用 reloadKey 作为 key 的一部分,确保 reloadKey 变化时强制重新挂载
140+
const componentKey = tab?.reloadKey ? `${key}-${tab.reloadKey}` : key;
141+
133142
components.push(
134-
<Activity key={key} mode={isVisible ? 'visible' : 'hidden'}>
143+
<Activity key={componentKey} mode={isVisible ? 'visible' : 'hidden'}>
135144
{cache.component}
136145
</Activity>
137146
);
@@ -140,15 +149,18 @@ const ActivityKeepAlive: React.FC<ActivityKeepAliveProps> = memo(({ children })
140149
// 如果当前页面未缓存(不需要 keepAlive),直接渲染
141150
const currentCached = cacheRef.current.get(activeKey);
142151
if (!currentCached && activeKey) {
152+
// 使用 reloadKey 确保组件在 reload 时重新挂载
153+
const componentKey = reloadKey ? `${activeKey}-${reloadKey}` : activeKey;
154+
143155
components.push(
144-
<Activity key={activeKey} mode="visible">
156+
<Activity key={componentKey} mode="visible">
145157
{children}
146158
</Activity>
147159
);
148160
}
149161

150162
return components;
151-
}, [activeKey, children, tabs.length]); // 依赖 tabs.length 触发更新
163+
}, [activeKey, children, tabs.length, reloadKey]); // 添加 reloadKey 到依赖
152164

153165
return (
154166
<div ref={containerRef} className="h-full relative flex flex-col p-4">

src/stores/tabStore.ts

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import type { RouteItem } from '@/types/route';
12
import { create } from 'zustand';
23
import { persist, type PersistOptions } from 'zustand/middleware';
3-
import type { RouteItem } from '@/types/route';
44

55
export interface TabItem {
66
key: string;
@@ -10,6 +10,7 @@ export interface TabItem {
1010
closable: boolean;
1111
component?: React.ComponentType;
1212
route?: RouteItem;
13+
reloadKey?: number; // 用于强制重新加载的时间戳
1314
}
1415

1516
interface TabStore {
@@ -18,7 +19,7 @@ interface TabStore {
1819
// 当前激活的tab key
1920
activeKey: string;
2021
// 添加tab
21-
addTab: (tab: TabItem, options?: { insertAt?: 'head' | 'tail', activate?: boolean }) => void;
22+
addTab: (tab: TabItem, options?: { insertAt?: 'head' | 'tail'; activate?: boolean }) => void;
2223
// 移除tab
2324
removeTab: (targetKey: string) => string;
2425
// 设置激活的tab
@@ -53,14 +54,14 @@ export const useTabStore = create<TabStore>()(
5354
tabs: [],
5455
activeKey: '',
5556

56-
addTab: (tab: TabItem, options?: { insertAt?: 'head' | 'tail', activate?: boolean }) => {
57+
addTab: (tab: TabItem, options?: { insertAt?: 'head' | 'tail'; activate?: boolean }) => {
5758
const { tabs, activeKey } = get();
5859
const existingTabIndex = tabs.findIndex((t) => t.key === tab.key);
5960

6061
if (existingTabIndex === -1) {
6162
// 新tab,根据选项决定插入位置
6263
const { insertAt = 'tail', activate = true } = options || {};
63-
64+
6465
let newTabs: TabItem[];
6566
if (insertAt === 'head') {
6667
// 头插入:添加到数组开头
@@ -101,10 +102,10 @@ export const useTabStore = create<TabStore>()(
101102
newActiveKey = '';
102103
} else if (targetIndex === 0) {
103104
// 关闭的是第一个,激活第一个
104-
newActiveKey = newTabs[0].key;
105+
newActiveKey = newTabs.length > 0 ? (newTabs[0]?.key ?? '') : '';
105106
} else {
106107
// 激活前一个
107-
newActiveKey = newTabs[targetIndex - 1].key;
108+
newActiveKey = newTabs[targetIndex - 1]?.key ?? '';
108109
}
109110
}
110111

@@ -134,9 +135,9 @@ export const useTabStore = create<TabStore>()(
134135
if (homeTab && homeTab.key !== targetKey) {
135136
newTabs.push(homeTab);
136137
}
137-
138+
138139
// 如果当前激活的tab不在保留的tab中,需要激活目标tab
139-
const newActiveKey = newTabs.some(tab => tab.key === activeKey) ? activeKey : targetKey;
140+
const newActiveKey = newTabs.some((tab) => tab.key === activeKey) ? activeKey : targetKey;
140141
set({
141142
tabs: newTabs,
142143
activeKey: newActiveKey,
@@ -151,20 +152,20 @@ export const useTabStore = create<TabStore>()(
151152
const targetIndex = tabs.findIndex((tab) => tab.key === targetKey);
152153
if (targetIndex > 0) {
153154
let newTabs = tabs.slice(targetIndex);
154-
155+
155156
// 如果homePath的tab在左侧被删除了,需要保留它
156157
if (homePath) {
157158
const homeTab = tabs.find((tab) => tab.key === homePath);
158-
if (homeTab && !newTabs.some(tab => tab.key === homePath)) {
159+
if (homeTab && !newTabs.some((tab) => tab.key === homePath)) {
159160
newTabs = [homeTab, ...newTabs];
160161
}
161162
}
162-
163+
163164
// 如果当前激活的tab不在保留的tab中,需要激活目标tab
164-
const newActiveKey = newTabs.some(tab => tab.key === activeKey) ? activeKey : targetKey;
165-
set({
165+
const newActiveKey = newTabs.some((tab) => tab.key === activeKey) ? activeKey : targetKey;
166+
set({
166167
tabs: newTabs,
167-
activeKey: newActiveKey
168+
activeKey: newActiveKey,
168169
});
169170
return newActiveKey;
170171
}
@@ -176,20 +177,20 @@ export const useTabStore = create<TabStore>()(
176177
const targetIndex = tabs.findIndex((tab) => tab.key === targetKey);
177178
if (targetIndex >= 0 && targetIndex < tabs.length - 1) {
178179
let newTabs = tabs.slice(0, targetIndex + 1);
179-
180+
180181
// 如果homePath的tab在右侧被删除了,需要保留它
181182
if (homePath) {
182183
const homeTab = tabs.find((tab) => tab.key === homePath);
183-
if (homeTab && !newTabs.some(tab => tab.key === homePath)) {
184+
if (homeTab && !newTabs.some((tab) => tab.key === homePath)) {
184185
newTabs.push(homeTab);
185186
}
186187
}
187-
188+
188189
// 如果当前激活的tab不在保留的tab中,需要激活目标tab
189-
const newActiveKey = newTabs.some(tab => tab.key === activeKey) ? activeKey : targetKey;
190-
set({
190+
const newActiveKey = newTabs.some((tab) => tab.key === activeKey) ? activeKey : targetKey;
191+
set({
191192
tabs: newTabs,
192-
activeKey: newActiveKey
193+
activeKey: newActiveKey,
193194
});
194195
return newActiveKey;
195196
}
@@ -198,31 +199,43 @@ export const useTabStore = create<TabStore>()(
198199

199200
closeAllTabs: (homePath?: string) => {
200201
const { tabs } = get();
201-
202+
202203
if (homePath) {
203204
// 保留homePath的tab
204205
const homeTab = tabs.find((tab) => tab.key === homePath);
205206
if (homeTab) {
206-
set({
207-
tabs: [homeTab],
208-
activeKey: homePath
207+
set({
208+
tabs: [homeTab],
209+
activeKey: homePath,
209210
});
210211
return homePath;
211212
}
212213
}
213-
214+
214215
// 如果没有homePath或找不到homeTab,清空所有tabs
215216
set({ tabs: [], activeKey: '' });
216217
return '';
217218
},
218219

219220
reloadTab: (targetKey: string) => {
220-
// 这里可以通过重新渲染组件来实现重新加载
221-
// 暂时只是重新设置activeKey来触发重新渲染
222-
const { activeKey } = get();
223-
if (targetKey === activeKey) {
224-
set({ activeKey: '' });
225-
setTimeout(() => set({ activeKey: targetKey }), 0);
221+
const { tabs } = get();
222+
223+
// 1. 清除 KeepAlive 缓存
224+
if (typeof window !== 'undefined' && (window as any).__keepAliveClearCache) {
225+
(window as any).__keepAliveClearCache(targetKey);
226+
}
227+
228+
// 2. 更新 tab 的 reloadKey,强制重新挂载组件
229+
const newTabs = tabs.map((tab) => (tab.key === targetKey ? { ...tab, reloadKey: Date.now() } : tab));
230+
231+
set({ tabs: newTabs });
232+
233+
// 3. 触发页面重新渲染(如果是当前激活的 tab)
234+
// 通过重新导航到同一路径来触发 TanStack Router 的重新加载
235+
if (window.location.pathname === targetKey) {
236+
// 使用 window.location.reload() 会刷新整个页面,这里我们不需要
237+
// TanStack Router 会自动处理组件的重新渲染
238+
console.log('🔄 重新加载 tab:', targetKey);
226239
}
227240
},
228241

@@ -255,6 +268,6 @@ export const useTabStore = create<TabStore>()(
255268
{
256269
name: 'tab-store',
257270
getStorage: () => localStorage,
258-
} as PersistOptions<TabStore>,
259-
),
271+
} as PersistOptions<TabStore>
272+
)
260273
);

0 commit comments

Comments
 (0)