[st] feat: tab store에 persist 적용
This commit is contained in:
@@ -1,20 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useCallback, useMemo } from "react";
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useQueryClient, useQuery } from "@tanstack/react-query";
|
||||
|
||||
import type { MenuItem } from "@/entities/menu";
|
||||
import { useMenuStore, useAlertStore } from "@/shared/lib";
|
||||
import { useTabStore, getTabStoreState } from "@/store/tab-store";
|
||||
import { useTabStore as gridTabStore } from "@rsone/components";
|
||||
import { getConfig } from "@common/config";
|
||||
import { getCookie } from "@/shared/lib";
|
||||
import { getMenuList } from "../../../../packages/common/components/src/api";
|
||||
import {
|
||||
getTabStoreState as getGridTabStoreState,
|
||||
useTabStore as gridTabStore,
|
||||
} from "@rsone/components";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
|
||||
import { BaseLayout } from "@common/components";
|
||||
import { getCookie, useAlertStore, useMenuStore } from "@/shared/lib";
|
||||
import { StatisticsPageLayout } from "@/shared/ui/statistics-page-layout";
|
||||
import type { MenuItem } from "@/entities/menu";
|
||||
import { BaseLayout } from "@common/components";
|
||||
import { getConfig } from "@common/config";
|
||||
|
||||
import { getMenuList } from "../../../../packages/common/components/src/api";
|
||||
|
||||
import { getTabStoreState, useTabStore } from "@/store/tab-store";
|
||||
|
||||
const appConfig = getConfig();
|
||||
const userId = getCookie(appConfig.AUTH_USER_ID ?? "");
|
||||
@@ -22,118 +26,147 @@ const appSiteId = getCookie("site-id-key") ?? "";
|
||||
const isLoggedIn = !!userId;
|
||||
|
||||
export default function WithBaseLayout({
|
||||
children,
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const queryClient = useQueryClient();
|
||||
const pathname = usePathname();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const setCurrentMenuInfo = useMenuStore((state) => state.setCurrentMenuInfo);
|
||||
const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo);
|
||||
const setCurrentMenuInfo = useMenuStore(
|
||||
(state) => state.setCurrentMenuInfo,
|
||||
);
|
||||
const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo);
|
||||
|
||||
const addTab = useTabStore((state) => state.addTab);
|
||||
const setActiveTab = useTabStore((state) => state.setActiveTab);
|
||||
const setMenuList = useTabStore((state) => state.setMenuList);
|
||||
const activeTabId = useTabStore((state) => state.activeTabId);
|
||||
const menuList = useTabStore((state) => state.menuList);
|
||||
const tabs = useTabStore((state) => state.tabs);
|
||||
const addTabForGrid = gridTabStore((state) => state.addTab);
|
||||
const setActiveTabForGrid = gridTabStore((state) => state.setActiveTab);
|
||||
const setMenuListForGrid = gridTabStore((state) => state.setMenuList);
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
const addTab = useTabStore((state) => state.addTab);
|
||||
const setActiveTab = useTabStore((state) => state.setActiveTab);
|
||||
const setMenuList = useTabStore((state) => state.setMenuList);
|
||||
const activeTabId = useTabStore((state) => state.activeTabId);
|
||||
const menuList = useTabStore((state) => state.menuList);
|
||||
const tabs = useTabStore((state) => state.tabs);
|
||||
const addTabForGrid = gridTabStore((state) => state.addTab);
|
||||
const setActiveTabForGrid = gridTabStore((state) => state.setActiveTab);
|
||||
const setMenuListForGrid = gridTabStore((state) => state.setMenuList);
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
|
||||
const { data: menuListData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
queryFn: () => getMenuList(appSiteId),
|
||||
enabled: isLoggedIn,
|
||||
});
|
||||
const { data: menuListData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
|
||||
queryFn: () => getMenuList(appSiteId),
|
||||
enabled: isLoggedIn,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (menuListData) {
|
||||
setMenuList(menuListData);
|
||||
setMenuListForGrid(menuListData);
|
||||
}
|
||||
}, [menuListData, setMenuList, setMenuListForGrid]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pathname) return;
|
||||
|
||||
// pathname이 변경될 때만 실행되도록 tabs를 직접 가져옴
|
||||
const currentTabs = getTabStoreState().tabs;
|
||||
const tab = currentTabs.find((t) => t.path === pathname);
|
||||
if (tab) {
|
||||
setActiveTab(tab.id);
|
||||
setActiveTabForGrid(tab.id);
|
||||
}
|
||||
}, [pathname, setActiveTab, setActiveTabForGrid]);
|
||||
|
||||
// 활성 탭의 메뉴를 찾아서 urlPath를 가져옴. 탭이 전부 닫히면 ''를 넘겨 LNB에서 메뉴 강조 제거
|
||||
const currentPath = useMemo(() => {
|
||||
if (tabs.length === 0) return "";
|
||||
|
||||
if (activeTabId && menuList) {
|
||||
// menuList에서 활성 탭의 메뉴 찾기
|
||||
const findMenuByEngNm = (items: MenuItem[]): MenuItem | null => {
|
||||
for (const item of items) {
|
||||
if (item.urlPath === activeTabId) {
|
||||
return item;
|
||||
}
|
||||
if (item.children) {
|
||||
const found = findMenuByEngNm(item.children);
|
||||
if (found) return found;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (menuListData) {
|
||||
setMenuList(menuListData);
|
||||
setMenuListForGrid(menuListData);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}, [menuListData, setMenuList, setMenuListForGrid]);
|
||||
|
||||
const activeMenu = findMenuByEngNm(menuList);
|
||||
// 새로고침 후 persist로 앱 탭만 복원된 경우 rsone 그리드 스토어에 탭을 맞춤
|
||||
useEffect(() => {
|
||||
if (!menuList) return;
|
||||
const appTabs = getTabStoreState().tabs;
|
||||
if (appTabs.length === 0) return;
|
||||
if (getGridTabStoreState().tabs.length > 0) return;
|
||||
|
||||
if (activeMenu?.urlPath) {
|
||||
return activeMenu.urlPath;
|
||||
}
|
||||
}
|
||||
for (const tab of appTabs) {
|
||||
getGridTabStoreState().addTab(tab.menuItem);
|
||||
}
|
||||
|
||||
// 탭에서 직접 찾기
|
||||
if (activeTabId) {
|
||||
const currentTabs = getTabStoreState().tabs;
|
||||
const activeTab = currentTabs.find((t) => t.id === activeTabId);
|
||||
if (activeTab?.menuItem?.urlPath) {
|
||||
return activeTab.menuItem.urlPath;
|
||||
}
|
||||
}
|
||||
const activeId = getTabStoreState().activeTabId;
|
||||
if (activeId) {
|
||||
const activeTab = appTabs.find((t) => t.id === activeId);
|
||||
const gridActiveId = activeTab?.menuItem.menuEngNm;
|
||||
if (gridActiveId) {
|
||||
getGridTabStoreState().setActiveTab(gridActiveId);
|
||||
}
|
||||
}
|
||||
}, [menuList, tabs]);
|
||||
|
||||
// currentMenuInfo에서 가져오기
|
||||
if (currentMenuInfo?.urlPath) {
|
||||
return currentMenuInfo.urlPath;
|
||||
}
|
||||
useEffect(() => {
|
||||
if (!pathname) return;
|
||||
|
||||
// 기본값으로 pathname 사용
|
||||
return pathname ?? "";
|
||||
}, [tabs.length, activeTabId, menuList, currentMenuInfo, pathname]);
|
||||
// pathname이 변경될 때만 실행되도록 tabs를 직접 가져옴
|
||||
const currentTabs = getTabStoreState().tabs;
|
||||
const tab = currentTabs.find((t) => t.path === pathname);
|
||||
if (tab) {
|
||||
setActiveTab(tab.id);
|
||||
const gridActiveId = tab.menuItem.menuEngNm;
|
||||
if (gridActiveId) {
|
||||
setActiveTabForGrid(gridActiveId);
|
||||
}
|
||||
}
|
||||
}, [pathname, setActiveTab, setActiveTabForGrid]);
|
||||
|
||||
const handleMenuClick = useCallback((item: MenuItem) => {
|
||||
setCurrentMenuInfo(item);
|
||||
const addTabResult: boolean = addTab(item);
|
||||
|
||||
// packages/rsone의 스토어에도 동기화
|
||||
if (addTabResult) {
|
||||
addTabForGrid(item);
|
||||
}
|
||||
// 활성 탭의 메뉴를 찾아서 urlPath를 가져옴. 탭이 전부 닫히면 ''를 넘겨 LNB에서 메뉴 강조 제거
|
||||
const currentPath = useMemo(() => {
|
||||
if (tabs.length === 0) return "";
|
||||
|
||||
if (!addTabResult) {
|
||||
alert(`최대 추가 할 수 있는 화면 수를 초과 하였습니다.
|
||||
if (activeTabId && menuList) {
|
||||
// menuList에서 활성 탭의 메뉴 찾기
|
||||
const findMenuByEngNm = (items: MenuItem[]): MenuItem | null => {
|
||||
for (const item of items) {
|
||||
if (item.urlPath === activeTabId) {
|
||||
return item;
|
||||
}
|
||||
if (item.children) {
|
||||
const found = findMenuByEngNm(item.children);
|
||||
if (found) return found;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const activeMenu = findMenuByEngNm(menuList);
|
||||
|
||||
if (activeMenu?.urlPath) {
|
||||
return activeMenu.urlPath;
|
||||
}
|
||||
}
|
||||
|
||||
// 탭에서 직접 찾기
|
||||
if (activeTabId) {
|
||||
const currentTabs = getTabStoreState().tabs;
|
||||
const activeTab = currentTabs.find((t) => t.id === activeTabId);
|
||||
if (activeTab?.menuItem?.urlPath) {
|
||||
return activeTab.menuItem.urlPath;
|
||||
}
|
||||
}
|
||||
|
||||
// currentMenuInfo에서 가져오기
|
||||
if (currentMenuInfo?.urlPath) {
|
||||
return currentMenuInfo.urlPath;
|
||||
}
|
||||
|
||||
// 기본값으로 pathname 사용
|
||||
return pathname ?? "";
|
||||
}, [tabs.length, activeTabId, menuList, currentMenuInfo, pathname]);
|
||||
|
||||
const handleMenuClick = useCallback(
|
||||
(item: MenuItem) => {
|
||||
setCurrentMenuInfo(item);
|
||||
const addTabResult: boolean = addTab(item);
|
||||
|
||||
// packages/rsone의 스토어에도 동기화
|
||||
if (addTabResult) {
|
||||
addTabForGrid(item);
|
||||
}
|
||||
|
||||
if (!addTabResult) {
|
||||
alert(`최대 추가 할 수 있는 화면 수를 초과 하였습니다.
|
||||
기존 화면을 닫은 후 이용해주세요.`);
|
||||
}
|
||||
}, [setCurrentMenuInfo, addTab, addTabForGrid, alert]);
|
||||
}
|
||||
},
|
||||
[setCurrentMenuInfo, addTab, addTabForGrid, alert],
|
||||
);
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
queryClient={queryClient}
|
||||
currentPath={currentPath}
|
||||
handleMenuClick={handleMenuClick}
|
||||
>
|
||||
<StatisticsPageLayout>{children}</StatisticsPageLayout>
|
||||
</BaseLayout>
|
||||
);
|
||||
return (
|
||||
<BaseLayout
|
||||
queryClient={queryClient}
|
||||
currentPath={currentPath}
|
||||
handleMenuClick={handleMenuClick}
|
||||
>
|
||||
<StatisticsPageLayout>{children}</StatisticsPageLayout>
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { AxiosError } from "axios";
|
||||
|
||||
import {
|
||||
ENDPOINT,
|
||||
JWT_EXPIRATION_TIME,
|
||||
ACCESS_TOKEN,
|
||||
AUTH_USER_ID,
|
||||
CLAIM_NAME,
|
||||
ENDPOINT,
|
||||
JWT_EXPIRATION_TIME,
|
||||
} from "@/shared/config";
|
||||
import type { LoginService, ILogin } from "../model/login-interface";
|
||||
import { getCookie } from "@/shared/lib";
|
||||
|
||||
import { tokenManager } from "../lib/token-manager";
|
||||
import type { ILogin,LoginService } from "../model/login-interface";
|
||||
|
||||
import { clearTabStorePersist } from "@/store/tab-store";
|
||||
|
||||
export class LoginApiService implements LoginService {
|
||||
private static instance: LoginApiService;
|
||||
@@ -39,6 +44,11 @@ export class LoginApiService implements LoginService {
|
||||
throw new Error("Missing required authentication tokens");
|
||||
}
|
||||
|
||||
const prevTokenId = getCookie(AUTH_USER_ID);
|
||||
if (prevTokenId !== undefined && prevTokenId !== tokenId) {
|
||||
clearTabStorePersist();
|
||||
}
|
||||
|
||||
tokenManager.setToken(CLAIM_NAME, accessToken);
|
||||
tokenManager.setToken(AUTH_USER_ID, tokenId);
|
||||
tokenManager.setupRefreshTimer(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { AUTH_USER_ID, CLAIM_NAME } from "@/shared/config";
|
||||
import { getCookie, setCookie, removeCookie } from "@/shared/lib";
|
||||
import { getCookie, removeCookie,setCookie } from "@/shared/lib";
|
||||
|
||||
import { clearTabStorePersist } from "@/store/tab-store";
|
||||
|
||||
export class TokenManager {
|
||||
private static instance: TokenManager;
|
||||
@@ -74,6 +76,7 @@ export class TokenManager {
|
||||
this.refreshTimer = null;
|
||||
}
|
||||
|
||||
clearTabStorePersist();
|
||||
this.removeToken(CLAIM_NAME);
|
||||
this.removeToken(AUTH_USER_ID);
|
||||
}
|
||||
|
||||
+153
-83
@@ -1,8 +1,10 @@
|
||||
import { create } from "zustand";
|
||||
import { createJSONStorage, persist } from "zustand/middleware";
|
||||
|
||||
import type { MenuItem } from "@/entities/menu";
|
||||
|
||||
export interface Tab {
|
||||
id: string; // 고유 ID (menuEngNm)
|
||||
id: string; // 고유 ID (urlPath)
|
||||
label: string; // 탭에 표시될 이름
|
||||
path: string; // 메뉴 경로
|
||||
urlPath: string; // url 경로
|
||||
@@ -22,96 +24,164 @@ interface TabStore {
|
||||
closeOtherTabs: (tabId: string) => void;
|
||||
}
|
||||
|
||||
const useTabStoreHook = create<TabStore>((set, get) => ({
|
||||
tabs: [],
|
||||
activeTabId: null,
|
||||
menuList: null,
|
||||
addTab: (menuItem: MenuItem) => {
|
||||
const { tabs, menuList } = get();
|
||||
const tabId = menuItem.urlPath || '';
|
||||
export const TAB_STORE_PERSIST_KEY = "st-tab-store";
|
||||
const TAB_STORE_VERSION = 1;
|
||||
const MAX_TABS = 10;
|
||||
|
||||
// 이미 존재하는 탭인지 확인
|
||||
const existingTab = tabs.find((tab) => tab.id === tabId);
|
||||
function tabForPersist(tab: Tab): Tab {
|
||||
return {
|
||||
...tab,
|
||||
menuItem: { ...tab.menuItem, children: null },
|
||||
};
|
||||
}
|
||||
|
||||
if (existingTab) {
|
||||
// 이미 존재하면 활성화만
|
||||
set({ activeTabId: tabId });
|
||||
return true;
|
||||
}
|
||||
function sanitizePersistedTabsAndActive(
|
||||
tabs: Tab[],
|
||||
activeTabId: string | null,
|
||||
): { tabs: Tab[]; activeTabId: string | null } {
|
||||
let nextTabs = Array.isArray(tabs) ? tabs : [];
|
||||
if (nextTabs.length > MAX_TABS) nextTabs = nextTabs.slice(0, MAX_TABS);
|
||||
|
||||
if (tabs.length < 10) {
|
||||
const newTab: Tab = {
|
||||
id: tabId,
|
||||
label: menuItem.menuKornNm,
|
||||
path: menuItem.menuPath,
|
||||
urlPath: menuItem.urlPath,
|
||||
parentEngNm: menuList?.find((m) => m.menuCd === menuItem.parentId)?.menuEngNm || '',
|
||||
menuItem,
|
||||
};
|
||||
|
||||
set({
|
||||
tabs: [...tabs, newTab],
|
||||
activeTabId: tabId,
|
||||
});
|
||||
if (nextTabs.length === 0) {
|
||||
return { tabs: [], activeTabId: null };
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
let nextActive = activeTabId;
|
||||
if (nextActive && !nextTabs.some((t) => t.id === nextActive)) {
|
||||
nextActive = nextTabs[0]?.id ?? null;
|
||||
}
|
||||
if (nextActive == null && nextTabs.length > 0) {
|
||||
nextActive = nextTabs[0]?.id ?? null;
|
||||
}
|
||||
|
||||
// 새 탭 추가
|
||||
},
|
||||
removeTab: (tabId: string): void => {
|
||||
const { tabs, activeTabId } = get();
|
||||
const newTabs = tabs.filter((tab) => tab.id !== tabId);
|
||||
return { tabs: nextTabs, activeTabId: nextActive };
|
||||
}
|
||||
|
||||
// 닫는 탭이 현재 활성 탭이면 다른 탭으로 전환
|
||||
let newActiveTabId = activeTabId;
|
||||
if (activeTabId === tabId) {
|
||||
const closedTabIndex = tabs.findIndex((tab) => tab.id === tabId);
|
||||
if (newTabs.length > 0) {
|
||||
// 닫힌 탭의 이전 탭을 활성화, 없으면 다음 탭
|
||||
if (closedTabIndex > 0) {
|
||||
const prevTab = tabs[closedTabIndex - 1];
|
||||
newActiveTabId = prevTab ? prevTab.id : null;
|
||||
} else {
|
||||
const firstTab = newTabs[0];
|
||||
newActiveTabId = firstTab ? firstTab.id : null;
|
||||
}
|
||||
} else {
|
||||
newActiveTabId = null;
|
||||
}
|
||||
}
|
||||
|
||||
set({
|
||||
tabs: newTabs,
|
||||
activeTabId: newActiveTabId,
|
||||
});
|
||||
},
|
||||
setActiveTab: (tabId: string) => {
|
||||
set({ activeTabId: tabId });
|
||||
},
|
||||
setMenuList: (menuList: MenuItem[] | null) => {
|
||||
set({ menuList });
|
||||
},
|
||||
closeAllTabs: () => {
|
||||
set({
|
||||
const useTabStoreHook = create<TabStore>()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
tabs: [],
|
||||
activeTabId: null,
|
||||
});
|
||||
},
|
||||
closeOtherTabs: (tabId: string) => {
|
||||
const { tabs } = get();
|
||||
const tabToKeep = tabs.find((tab) => tab.id === tabId);
|
||||
menuList: null,
|
||||
addTab: (menuItem: MenuItem) => {
|
||||
const { tabs, menuList } = get();
|
||||
const tabId = menuItem.urlPath || "";
|
||||
|
||||
if (tabToKeep) {
|
||||
set({
|
||||
tabs: [tabToKeep],
|
||||
activeTabId: tabId,
|
||||
});
|
||||
}
|
||||
},
|
||||
}));
|
||||
const existingTab = tabs.find((tab) => tab.id === tabId);
|
||||
|
||||
if (existingTab) {
|
||||
set({ activeTabId: tabId });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tabs.length < MAX_TABS) {
|
||||
const newTab: Tab = {
|
||||
id: tabId,
|
||||
label: menuItem.menuKornNm,
|
||||
path: menuItem.menuPath,
|
||||
urlPath: menuItem.urlPath,
|
||||
parentEngNm:
|
||||
menuList?.find(
|
||||
(m) => m.menuCd === menuItem.parentId,
|
||||
)?.menuEngNm || "",
|
||||
menuItem,
|
||||
};
|
||||
|
||||
set({
|
||||
tabs: [...tabs, newTab],
|
||||
activeTabId: tabId,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
removeTab: (tabId: string): void => {
|
||||
const { tabs, activeTabId } = get();
|
||||
const newTabs = tabs.filter((tab) => tab.id !== tabId);
|
||||
|
||||
let newActiveTabId = activeTabId;
|
||||
if (activeTabId === tabId) {
|
||||
const closedTabIndex = tabs.findIndex(
|
||||
(tab) => tab.id === tabId,
|
||||
);
|
||||
if (newTabs.length > 0) {
|
||||
if (closedTabIndex > 0) {
|
||||
const prevTab = tabs[closedTabIndex - 1];
|
||||
newActiveTabId = prevTab ? prevTab.id : null;
|
||||
} else {
|
||||
const firstTab = newTabs[0];
|
||||
newActiveTabId = firstTab ? firstTab.id : null;
|
||||
}
|
||||
} else {
|
||||
newActiveTabId = null;
|
||||
}
|
||||
}
|
||||
|
||||
set({
|
||||
tabs: newTabs,
|
||||
activeTabId: newActiveTabId,
|
||||
});
|
||||
},
|
||||
setActiveTab: (tabId: string) => {
|
||||
set({ activeTabId: tabId });
|
||||
},
|
||||
setMenuList: (menuList: MenuItem[] | null) => {
|
||||
set({ menuList });
|
||||
},
|
||||
closeAllTabs: () => {
|
||||
set({
|
||||
tabs: [],
|
||||
activeTabId: null,
|
||||
});
|
||||
},
|
||||
closeOtherTabs: (tabId: string) => {
|
||||
const { tabs } = get();
|
||||
const tabToKeep = tabs.find((tab) => tab.id === tabId);
|
||||
|
||||
if (tabToKeep) {
|
||||
set({
|
||||
tabs: [tabToKeep],
|
||||
activeTabId: tabId,
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: TAB_STORE_PERSIST_KEY,
|
||||
version: TAB_STORE_VERSION,
|
||||
storage: createJSONStorage(() => localStorage),
|
||||
partialize: (state) => ({
|
||||
tabs: state.tabs.map(tabForPersist),
|
||||
activeTabId: state.activeTabId,
|
||||
}),
|
||||
merge: (persistedState, currentState) => {
|
||||
const p = persistedState as Partial<
|
||||
Pick<TabStore, "tabs" | "activeTabId">
|
||||
> | null;
|
||||
const rawTabs = p?.tabs;
|
||||
const rawActive = p?.activeTabId;
|
||||
const { tabs, activeTabId } = sanitizePersistedTabsAndActive(
|
||||
(rawTabs as Tab[]) ?? [],
|
||||
typeof rawActive === "string" || rawActive === null
|
||||
? rawActive
|
||||
: null,
|
||||
);
|
||||
return {
|
||||
...currentState,
|
||||
tabs,
|
||||
activeTabId,
|
||||
};
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
export const useTabStore = useTabStoreHook;
|
||||
export const getTabStoreState = () => useTabStoreHook.getState();
|
||||
export const getTabStoreState = () => useTabStoreHook.getState();
|
||||
|
||||
// 로그아웃 또는 사용자 전환 시 로컬 탭 스냅샷 제거 및 메모리 상태 초기화
|
||||
export function clearTabStorePersist(): void {
|
||||
useTabStoreHook.getState().closeAllTabs();
|
||||
useTabStoreHook.persist.clearStorage();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user