"use client" import { useState, useEffect, useRef, useCallback } from "react" import { getKnowledgeGraphDataAction } from "../actions" import type { GraphViewMode, KnowledgeGraphData } from "../types" interface UseGraphDataResult { data: KnowledgeGraphData | null isLoading: boolean error: string | null reload: () => void } /** * 图谱数据加载 Hook。 * * 按 textbookId + viewMode 加载,切换 viewMode 时重新加载。 * 使用派生值模式(isLoading 从 data.viewMode 派生),避免 effect 中同步 setState。 */ export function useGraphData( textbookId: string, viewMode: GraphViewMode, ): UseGraphDataResult { const [data, setData] = useState(null) const [error, setError] = useState(null) const [reloadTrigger, setReloadTrigger] = useState(0) const lastRequestKey = useRef("") const reload = useCallback(() => { setReloadTrigger((n) => n + 1) }, []) // 派生 loading 状态:无数据或当前数据不匹配请求的 viewMode const isLoading = data === null || data.viewMode !== viewMode useEffect(() => { if (!textbookId) return const requestKey = `${textbookId}:${viewMode}:${reloadTrigger}` if (lastRequestKey.current === requestKey) return lastRequestKey.current = requestKey let cancelled = false getKnowledgeGraphDataAction(textbookId, viewMode) .then((result) => { if (cancelled) return if (result.success && result.data) { setData(result.data) setError(null) } else { setData(null) setError(result.message ?? "Unknown error") } }) .catch((e: unknown) => { if (!cancelled) { setData(null) setError(e instanceof Error ? e.message : "Unknown error") } }) return () => { cancelled = true } }, [textbookId, viewMode, reloadTrigger]) return { data, isLoading, error, reload } }