仅返回辅助函数的自定义Hook是否合规?最佳实践探讨
问题:仅返回辅助函数的自定义Hook是否属于常见实践?哪种方案更优?
我编写了一个仅返回辅助函数的自定义Hook,该Hook未使用useState、useEffect等任何其他Hook,示例代码如下:
import backend from '../lib/backend'; import axios, { AxiosError } from 'axios'; interface PresignedData { fields: any; url: string; key: string; } type Resource = 'users' | 'events'; function useBucket() { const uploadViaPresignedPost = async function ( resource: Resource, file: File ) { ... }; const buildImageUrl = function (key: string) { return `${process.env.NEXT_PUBLIC_S3_BUCKET_DOMAIN}/${key}`; }; return { uploadViaPresignedPost, buildImageUrl }; } export default useBucket;
请问这种做法是否属于常见实践?相比创建含静态方法的类、从单独文件导出辅助函数,哪种才是最佳实践?
回答
这种只返回辅助函数、完全没用到useState/useEffect等React内置Hook的自定义Hook写法,算不上常见实践。毕竟React设计自定义Hook的核心目的是复用带状态的逻辑,你这种写法本质就是把一堆普通函数包在了一个以use开头的函数里,反而容易让其他开发者困惑——按照社区约定,use开头的函数应该是符合Hook规则的,得关联状态或生命周期逻辑才对。
下面对比三种方案的优劣:
1. 单独文件导出辅助函数(首推)
这是最直接、轻量的方案,完全贴合JS/TS的模块化思路:
- 优点:代码简洁直白,没有多余封装;可以按需导入单个函数,减少冗余加载;语义清晰,谁都能一眼看出这是纯工具函数。
- 示例写法:
// lib/bucketUtils.ts import backend from '../lib/backend'; import axios, { AxiosError } from 'axios'; interface PresignedData { fields: any; url: string; key: string; } type Resource = 'users' | 'events'; export async function uploadViaPresignedPost(resource: Resource, file: File) { // ...你的实现逻辑 } export function buildImageUrl(key: string) { return `${process.env.NEXT_PUBLIC_S3_BUCKET_DOMAIN}/${key}`; }
用的时候直接按需导入:import { uploadViaPresignedPost } from '../lib/bucketUtils'
2. 带静态方法的类
这种写法在面向对象风格的代码里偶尔会见到,但在React生态里完全没必要:
- 缺点:类的封装纯属多此一举,静态方法本质和普通函数没区别,却平白多了一层类的壳;不符合React社区偏函数式的开发习惯;容易误导别人以为这个类需要实例化,造成语义混乱。
3. 无状态的自定义Hook
也就是你现在的写法,属于对自定义Hook的“误用”:
- 缺点:违背了React自定义Hook的设计初衷,团队里其他人看到
useBucket会下意识觉得它带状态逻辑,结果打开一看只是普通函数,反而增加理解成本;完全没用到Hook的核心能力,还多了一层函数调用的开销。
总结一下:如果你的工具函数不需要依赖React的状态或生命周期逻辑,单独导出辅助函数绝对是最佳实践——简单、清晰、没有任何多余的包袱。
内容的提问来源于stack exchange,提问作者anolan23
相关产品推荐
相关产品推荐

