54 lines
1.9 KiB
TypeScript
54 lines
1.9 KiB
TypeScript
import { useContext, useMemo } from "react";
|
||
import type { Client } from "urql";
|
||
|
||
/**
|
||
* useGraphQLClient - 获取 urql client 单例
|
||
*
|
||
* 用途:从 Shell 注入的 GraphQLProvider Context 中获取 urql client。
|
||
* 总裁裁决 §2.17 方案 A:Shell(teacher-portal)暴露 GraphQLProvider,
|
||
* Remote(student/parent/admin)复用同一 client 单例,避免多实例 + 缓存不一致。
|
||
*
|
||
* 注意:本 hook 必须在 GraphQLProvider 内部调用,否则抛出错误。
|
||
*
|
||
* @example
|
||
* const client = useGraphQLClient();
|
||
* const [result] = useQuery({ query: DashboardQuery });
|
||
*
|
||
* MF 暴露:ARB-002 §2.2 exposes './useGraphQLClient'
|
||
*/
|
||
|
||
// GraphQLClientContext 由 Shell(apps/teacher-portal/src/app/providers.tsx)创建并注入。
|
||
// 此处通过 createContext 占位,Shell 在初始化时通过 Provider value 覆盖。
|
||
// 为避免 hooks 包直接依赖 urql(peerDependency),Context 值类型用 Client(urql 类型)。
|
||
// urql 在 apps/* 的 dependencies 中,通过 peerDependencies 声明。
|
||
|
||
import { createContext } from "react";
|
||
|
||
/**
|
||
* GraphQL Client Context
|
||
*
|
||
* Shell(teacher-portal)通过 <Context.Provider value={client}> 注入。
|
||
* Remote(student/parent/admin-portal)通过 MF shared 复用同一 Context 实例。
|
||
*/
|
||
export const GraphQLClientContext = createContext<Client | null>(null);
|
||
|
||
GraphQLClientContext.displayName = "GraphQLClientContext";
|
||
|
||
export interface UseGraphQLClientReturn {
|
||
/** urql client 单例 */
|
||
client: Client;
|
||
}
|
||
|
||
export function useGraphQLClient(): UseGraphQLClientReturn {
|
||
const client = useContext(GraphQLClientContext);
|
||
|
||
if (!client) {
|
||
throw new Error(
|
||
"useGraphQLClient 必须在 GraphQLProvider 内部调用。" +
|
||
"请确保组件树被 <GraphQLProvider> 包裹(apps/teacher-portal/src/app/providers.tsx)。",
|
||
);
|
||
}
|
||
|
||
return useMemo(() => ({ client }), [client]);
|
||
}
|