跳到主要内容

VEF Hooks

这里是 @vef-framework-react/hooks 维护的每个 hook 的详尽签名级索引。若需要叙述性的“何时用什么”版本,请参阅 Hooks。该包的另一半——经同一入口重新导出的 Mantine 与快捷键 hooks——收录在上游 Hook 导出中。

权限 Hooks

HookSignatureDescription
useCheckPermission() => (requiredPermissions?: MaybeArray<string>, checkMode?: PermissionCheckMode) => boolean返回一个稳定的函数,用于执行命令式的权限检查(例如在事件处理器内部)。
useIsAuthorized(requiredPermissions?: MaybeArray<string>, checkMode?: PermissionCheckMode) => boolean在渲染阶段直接检查权限——即 useCheckPermission()(...) 的 hook 形式。
useAuthorizedItems<T extends PermissionAware>(items: T[]) => T[]将配置数组过滤为当前用户有权限访问的项。PermissionAware{ requiredPermissions?: MaybeArray<string>; checkMode?: PermissionCheckMode }(默认 checkMode: "any")。

这三个 hook 都从 AppContext 中读取 hasPermission(参见上下文提供者),当应用上下文未提供该函数时默认使用 () => true。空值(nullish)的 requiredPermissions 始终通过。

码集与选项 Hooks

HookSignatureDescription
useCodeSetQuery<const T extends CodeSetAliasMap, TData = CodeSetQueryData<T>>(keys: T, options?: UseCodeSetQueryOptions<T, TData>) => UseQueryResult<TData>通过 appContext.codeSetQueryFn 获取宿主码集选项,以别名映射(例如 { gender: "common.gender" })为键;每个别名都会成为解析后 data 中的一个 key,当后端未返回对应码集时默认为 []。若未配置 codeSetQueryFn 则抛出异常。data 遵循 React Query 语义——在查询完成前为 undefined。由 useDictionaryQuery 重命名而来;参见码集指南。在下拉选择场景中,优先使用对该 hook 做了封装的 useCodeSetOptionsSelect(来自 @vef-framework-react/components)。
resolveCodeSetKey(value: CodeSetKeyValue) => string从纯字符串或 CodeSetKeyConfig 对象中提取码集 key(由 resolveDictKey 重命名而来)。
useDataOptionsQuery<TQueryFnData, TData, TParams>(config: UseDataOptionsQueryOptions<TQueryFnData, TData, TParams>) => UseDataOptionsQueryResult<TData, DataOption<TData>>(或当 config.withPinyintrue 时的 DataOptionWithPinyin<TData>config.queryOptions 传入 useQuery 执行,并通过可配置的 labelKey / valueKey / disabledKey / descriptionKey / childrenKey 提取器(字符串路径或函数;默认 "label" / "value" / "disabled" / "description" / "children")将解析后的数组转换为 DataOption,并递归处理嵌套的 children。返回 { options, ...restOfQueryResult }useQuery 结果中除 data 外的所有字段)。

useCodeSetQuery 行为说明:

  • 查询键为 [codeSetQueryFn.key, sortedKeys],其中 sortedKeys 是解析后的码集 key 去重并排序后的列表——解析到相同 key 集合的别名映射共享同一个缓存条目。
  • 结果以 staleTime: Infinity 缓存;码集在会话期间被视为静态数据。
  • 空的 keys 映射会替换为 skipQueryToken,因此查询永远不会执行。
  • options.enabled(默认 true)用于推迟发起请求,例如上游参数尚未就绪时。
  • options.select 在别名解析完成后对解析结果做二次处理;其返回值成为 data。它会被透传给 React Query 的 select 记忆化机制,因此 keysselect 的引用稳定性由调用方负责——将它们放在模块作用域、使用 as constuseMemouseCallback,避免每次渲染都重新执行转换。

通过 Register 约束码集 Key

Register 是 hooks 包类型的扩展注册表:一个空接口,项目可通过模块扩充(module augmentation)对其扩展。声明了 codeSetKeys 成员后,CodeSetKey 会从 string 收窄为该联合类型——码集 key 中的拼写错误将成为编译错误:

declare module "@vef-framework-react/hooks" {
interface Register {
codeSetKeys: "sys.menu.type" | "sys.user.gender";
}
}

未来 hook 层面的扩展会作为同一注册表的成员加入(该 Register 扩充成员已由 dictionaryKeys 重命名为 codeSetKeys)。

类型

类型说明
CodeSetAliasMap{ readonly [alias: string]: CodeSetKeyValue }(不含数组/索引 key)——useCodeSetQuery 接受的别名映射结构。
CodeSetKey码集 key 的字符串类型;当项目扩充了它时会收窄为 Register["codeSetKeys"],否则为 string
CodeSetKeyConfig{ key: CodeSetKey; filterable?: boolean }——带有单 key 级别覆盖项的码集 key。
CodeSetKeyValueCodeSetKey | CodeSetKeyConfig
CodeSetQueryData<T>Record<Extract<keyof T, string>, DataOption[]>——useCodeSetQuerydata 解析后的结构。
Register空接口;可通过 declare module "@vef-framework-react/hooks" 做模块扩充,将 CodeSetKey 约束为一个已知的联合类型。
UseCodeSetQueryOptions<T, TData>{ enabled?: boolean; select?: (data: CodeSetQueryData<T>) => TData }
FieldExtractor<TData, TValue>string | ((item: TData) => TValue)——点路径字符串或取值函数。
UseDataOptionsQueryOptions<TQueryFnData, TData, TParams>{ queryOptions: UseQueryOptions<TQueryFnData[], TData[], TParams>; labelKey?; valueKey?; disabledKey?; descriptionKey?; childrenKey?; withPinyin?: boolean }
UseDataOptionsQueryResult<TData, TOption>Omit<UseQueryResult<TData[]>, "data"> & { options: TOption[] }

服务端推送 Hook

HookSignatureDescription
usePushMessage<TPayload = unknown>(client: PushClient, type: string, handler: PushMessageHandler<TPayload>) => void订阅一种消息信封类型的服务端推送消息,并自动清理(卸载时取消订阅)。将 type 传为 "*" 可接收所有消息。

client 通常是应用级单例(参见服务端推送参考服务端推送指南)。每当 clienttypehandler 的引用变化时都会重建订阅——请保持 handler 稳定(useCallback 或模块级函数),避免订阅反复重建。

import { usePushMessage } from "@vef-framework-react/hooks";

function OrderBadge({ pushClient }) {
usePushMessage(pushClient, "order.status_changed", message => {
console.log("Order updated:", message.payload);
});
}

深/浅比较 Hooks

当依赖项是引用会变但值不变的对象/数组时,可直接替换内置的依赖数组 hooks。它们都构建在 useDeepCompare / useShallowCompare 之上,两者仅在数组发生(深层或浅层)差异时才递增内部信号;undefined 依赖项始终视为发生变化,与不传数组的 useEffect(() => {}) 行为一致。

HookSignatureDescription
useDeepCompare(dependencies?: DependencyList) => readonly [number]useDeep* 系列所基于的基础函数;返回一个可用作依赖数组的单元素信号元组。
useDeepCallback<T extends Function>(callback: T, dependencies: DependencyList) => T带深层依赖比较的 useCallback
useDeepEffect(effect: EffectCallback, dependencies?: DependencyList) => void带深层依赖比较的 useEffect
useDeepIsomorphicEffect(effect: EffectCallback, dependencies?: DependencyList) => void客户端使用 useLayoutEffect、服务端使用 useEffect,并带深层依赖比较。
useDeepLayoutEffect(effect: EffectCallback, dependencies?: DependencyList) => void带深层依赖比较的 useLayoutEffect
useDeepMemo<T>(factory: () => T, dependencies: DependencyList) => T带深层依赖比较的 useMemo
useShallowCompare(dependencies?: DependencyList) => readonly [number]useShallow* 系列所基于的基础函数。
useShallowCallback<T extends Function>(callback: T, dependencies: DependencyList) => T带浅层依赖比较的 useCallback
useShallowEffect(effect: EffectCallback, dependencies?: DependencyList) => void带浅层依赖比较的 useEffect
useShallowIsomorphicEffect(effect: EffectCallback, dependencies?: DependencyList) => void客户端使用 useLayoutEffect、服务端使用 useEffect,并带浅层依赖比较。
useShallowLayoutEffect(effect: EffectCallback, dependencies?: DependencyList) => void带浅层依赖比较的 useLayoutEffect
useShallowMemo<T>(factory: () => T, dependencies: DependencyList) => T带浅层依赖比较的 useMemo

上传 Hook

HookSignatureDescription
useUpload(options?: UseUploadOptions) => UseUploadResult对 core 中支持分片、可续传的 Uploader 的 React 适配层,每个使用方同一时刻只作用于一个进行中的上传(再次调用 upload() 会取消上一次的执行)。如需并行/批量上传,请直接使用 @vef-framework-react/core 中的 Uploader

UseUploadOptions 对应 UploaderOptions(来自 core),去掉了由 React 接管的字段(signalonSessionOpened),并新增:

选项类型默认值说明
onProgress(progress: UploadProgress) => void每次聚合进度更新时触发
onStatusChange(status: UploadStatus) => void每次 Uploader 状态转换时触发
onSuccess(result: UploadResult) => void上传成功完成时触发一次
onError(error: UploadError) => void任意终态失败(包括中止)时触发一次
persistenceResumablePersistence | nullLocalStoragePersistence续传记录的持久化层;传入 null 可完全禁用续传
fingerprinterFileFingerprintercrypto.subtle 可用时为 PrefixFingerprinter,否则为 WeakFingerprinter跨会话的文件身份识别策略
onResumeDetectedResumeDecisionHandler丢弃发现续传候选时的决策处理函数;默认选择丢弃(续传错误的文件比重新上传更糟糕)

UseUploadResult

字段类型说明
upload(file: File | Blob, init?: UploadInit) => Promise<UploadResult>启动一次新上传,并取消仍在进行中的上传。普通 Blob 输入(没有 File 元数据)会绕过续传规划器,始终重新上传
abort() => void取消进行中的上传(包括启动前的续传规划阶段);空闲/终态时为无操作
reset() => void清空状态并解绑当前的 Uploader
statusUploadStatus当前状态;两次重置之间为 "idle"
progressUploadProgress最新的聚合进度;重置之间归零。在续传路径上,首次进度从已完成的字节数开始——应把它当作起始位置,而不是进度回退
errorUploadError | null状态为 "failed" 时的终态错误;与 result 互斥
resultUploadResult | null状态为 "succeeded" 时的终态结果
isUploadingboolean状态是否为 initializing / uploading / completing / aborting 之一

行为说明:持久化的续传记录在后端确认会话后立即写入,上传成功与用户中止时删除(失败时保留,以便重试可以续传);组件卸载时会自动中止进行中的上传。

事件与环境 Hooks

HookSignatureDescription
useDocumentEvent<TType extends string>(type: TType, listener: TType extends keyof DocumentEventMap ? (this: Document, event: DocumentEventMap[TType]) => void : (this: Document, event: CustomEvent) => void, options?: boolean | AddEventListenerOptions) => void添加一个 document 级别的事件监听器,并自动清理;已知事件名获得对应的原生事件类型,未知事件名按 CustomEvent 处理。监听器始终能看到最新的闭包(不存在闭包过期问题,且仅监听器变化时不会重新订阅)。
useEmitterEvent<TEvents extends Record<EventType, any>>(emitter: EventEmitter<TEvents>, eventType: keyof TEvents, eventListener: EventHandler<TEvents[keyof TEvents]>) => void订阅一个 sharedEventEmitter 事件,并自动清理。当 emittereventTypeeventListener 的引用变化时会重新订阅——请保持监听器稳定。
useLatest<T>(value: T) => MutableRefObject<T>始终持有最新 value 的 ref,用于在稳定的回调函数内部读取当前 props/state,而无需将其加入依赖数组。
useRafState<T>(initialState: T | (() => T)) => [T, Dispatch<SetStateAction<T>>]其 setter 通过 requestAnimationFrame 批量处理更新的 useState(待执行的帧会被下一次 set 以及卸载取消),适用于高频更新场景(滚动、resize、指针移动)。
useSingleton<T>(initializer: () => T) => RefObject<T>仅在首次渲染时创建一次值(例如 new EventEmitter()),并返回指向它的稳定 ref(初始化函数不得返回 undefined,该值会被视为“尚未初始化”)。
useViewportSize() => { width: number; height: number }跟踪 window.innerWidth / innerHeight,在 resize 和屏幕方向变化时通过被动监听器更新,并经 useRafState 批量处理。window 不可用(SSR)时返回 0 × 0
useBreakpoints<T extends string>(breakpoints: Breakpoints<T>, options?: UseBreakpointsOptions<T>) => UseBreakpointsResult<T>使用 min-width 媒体查询,跟踪当前匹配的命名断点。Breakpoints<T> = Record<T, number | string>(数字会转为 px);需包含一个宽度为 0 的条目以覆盖最小的视口。

useBreakpoints 细节:

  • UseBreakpointsResult<T>{ current?: T; value?: number | string; matches: T[] }——current 是当前匹配的最大断点名称,value 是其配置的宽度,matches 是按宽度升序排列的所有匹配名称。无匹配时均为空/undefined
  • UseBreakpointsOptions<T>{ initialBreakpoint?: T; getInitialValueInEffect?: boolean }initialBreakpoint 用于在 SSR 期间(或 effect 运行前)为结果提供种子值;getInitialValueInEffect(默认 false)将首次真实测量推迟到 effect 中,以避免水合不一致。

Query 与 Mutation 状态 Hooks

HookSignatureDescription
useHasFetching(key: string, params?: unknown) => boolean是否有键以 [key](提供 params 时为 [key, params];非精确匹配)开头的活跃查询正在请求中。
useHasMutating(key: string) => boolean是否有键以 [key] 开头(非精确匹配)的变更正在执行中。

两者都特别适用于页面级的加载状态协调,以及在请求进行期间禁用重复操作。