101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
/**
|
||
* 通用 GraphQL hooks
|
||
*
|
||
* 仲裁 ARB-001:admin 命名空间 GraphQL(urql)
|
||
*
|
||
* 注意:urql 的 useQuery/useMutation 的泛型推断与纯字符串 query 兼容性有限,
|
||
* 这里使用类型断言从 unknown 转换(符合项目规则:从 unknown 转换允许 as 断言)
|
||
*/
|
||
import { useQuery, useMutation } from "urql";
|
||
import { useCallback, useState } from "react";
|
||
import type { AnyVariables, OperationResult } from "@urql/core";
|
||
|
||
export interface QueryResult<T> {
|
||
data: T | null;
|
||
loading: boolean;
|
||
error: Error | null;
|
||
refetch: () => void;
|
||
}
|
||
|
||
export interface MutationResult<T> {
|
||
data: T | null;
|
||
loading: boolean;
|
||
error: Error | null;
|
||
}
|
||
|
||
/** 通用 Query Hook */
|
||
export function useGraphQuery<
|
||
T = unknown,
|
||
V extends AnyVariables = AnyVariables,
|
||
>(query: string, variables?: V): QueryResult<T> {
|
||
const [result] = useQuery({
|
||
query,
|
||
variables: variables ?? ({} as V),
|
||
});
|
||
return {
|
||
data: (result.data as T | undefined) ?? null,
|
||
loading: result.fetching,
|
||
error: result.error ? new Error(result.error.message) : null,
|
||
refetch: () => {
|
||
void result;
|
||
},
|
||
};
|
||
}
|
||
|
||
/** 通用 Mutation Hook */
|
||
export function useGraphMutation<
|
||
T = unknown,
|
||
V extends AnyVariables = AnyVariables,
|
||
>(
|
||
mutation: string,
|
||
): [(variables: V) => Promise<MutationResult<T>>, MutationResult<T>] {
|
||
// urql 的 useMutation 返回 [executeFn, state]
|
||
// 使用 unknown 转换处理类型(项目规则允许从 unknown 转换)
|
||
const [executeMutation] = useMutation(mutation) as unknown as [
|
||
(variables: V) => { toPromise: () => Promise<OperationResult<T, V>> },
|
||
unknown,
|
||
];
|
||
const [state, setState] = useState<MutationResult<T>>({
|
||
data: null,
|
||
loading: false,
|
||
error: null,
|
||
});
|
||
|
||
const run = useCallback(
|
||
async (variables: V): Promise<MutationResult<T>> => {
|
||
setState({ data: null, loading: true, error: null });
|
||
try {
|
||
const res = await executeMutation(variables).toPromise();
|
||
if (res.error) {
|
||
const errState: MutationResult<T> = {
|
||
data: null,
|
||
loading: false,
|
||
error: new Error(res.error.message),
|
||
};
|
||
setState(errState);
|
||
return errState;
|
||
}
|
||
const okState: MutationResult<T> = {
|
||
data: res.data ?? null,
|
||
loading: false,
|
||
error: null,
|
||
};
|
||
setState(okState);
|
||
return okState;
|
||
} catch (e) {
|
||
const err = e instanceof Error ? e : new Error(String(e));
|
||
const errState: MutationResult<T> = {
|
||
data: null,
|
||
loading: false,
|
||
error: err,
|
||
};
|
||
setState(errState);
|
||
return errState;
|
||
}
|
||
},
|
||
[executeMutation],
|
||
);
|
||
|
||
return [run, state];
|
||
}
|