perf(codebase): implement in-memory cache for file tree to persist state across tab navigations
This commit is contained in:
@@ -38,21 +38,50 @@ interface GiteaFileTreeProps {
|
||||
selectedPath?: string;
|
||||
}
|
||||
|
||||
// ── In-memory cache to persist tree state across tab navigations ──
|
||||
const treeCache: Record<
|
||||
string,
|
||||
{
|
||||
rootItems: TreeItem[];
|
||||
childrenByPath: Record<string, TreeItem[]>;
|
||||
expanded: Set<string>;
|
||||
}
|
||||
> = {};
|
||||
|
||||
export function GiteaFileTree({
|
||||
projectId,
|
||||
rootPath,
|
||||
onSelectFile,
|
||||
selectedPath,
|
||||
}: GiteaFileTreeProps) {
|
||||
const [rootItems, setRootItems] = useState<TreeItem[] | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const cacheKey = `${projectId}::${rootPath}`;
|
||||
|
||||
const [rootItems, setRootItems] = useState<TreeItem[] | null>(() => {
|
||||
return treeCache[cacheKey]?.rootItems ?? null;
|
||||
});
|
||||
const [loading, setLoading] = useState(() => !treeCache[cacheKey]);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [expanded, setExpanded] = useState<Set<string>>(new Set());
|
||||
const [expanded, setExpanded] = useState<Set<string>>(() => {
|
||||
return treeCache[cacheKey]?.expanded ?? new Set();
|
||||
});
|
||||
const [childrenByPath, setChildrenByPath] = useState<
|
||||
Record<string, TreeItem[]>
|
||||
>({});
|
||||
>(() => {
|
||||
return treeCache[cacheKey]?.childrenByPath ?? {};
|
||||
});
|
||||
const [loadingPaths, setLoadingPaths] = useState<Set<string>>(new Set());
|
||||
|
||||
// Keep cache synced with state updates
|
||||
useEffect(() => {
|
||||
if (rootItems) {
|
||||
treeCache[cacheKey] = {
|
||||
rootItems,
|
||||
childrenByPath,
|
||||
expanded,
|
||||
};
|
||||
}
|
||||
}, [cacheKey, rootItems, childrenByPath, expanded]);
|
||||
|
||||
const fetchPath = useCallback(
|
||||
async (path: string): Promise<TreeItem[]> => {
|
||||
const res = await fetch(
|
||||
@@ -71,12 +100,12 @@ export function GiteaFileTree({
|
||||
|
||||
// Load root whenever projectId or rootPath changes
|
||||
useEffect(() => {
|
||||
// If we already loaded this from cache on mount, skip fetching again
|
||||
if (treeCache[cacheKey]) return;
|
||||
|
||||
let cancelled = false;
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setRootItems(null);
|
||||
setExpanded(new Set());
|
||||
setChildrenByPath({});
|
||||
|
||||
fetchPath(rootPath)
|
||||
.then(async (items) => {
|
||||
@@ -119,7 +148,7 @@ export function GiteaFileTree({
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [projectId, rootPath, fetchPath]);
|
||||
}, [projectId, rootPath, fetchPath, cacheKey]);
|
||||
|
||||
const toggleDir = useCallback(
|
||||
async (path: string) => {
|
||||
|
||||
Reference in New Issue
Block a user