React中如何在首次渲染与表单校验中复用自定义useQuery钩子?
解决方案
首先明确核心限制:React钩子(包括你自定义的useSomethingQuery)只能在组件或其他自定义钩子的顶层调用,不能在useEffect、校验器这类嵌套函数内使用。我们可以通过以下方式复用useQuery的逻辑,同时满足首次渲染和校验器的需求:
1. 组件顶层调用自定义钩子
在组件最外层调用useSomethingQuery,获取数据、手动刷新方法和加载状态:
import { useState, useEffect } from 'react'; import { Form } from 'antd'; import { useSomethingQuery } from './your-hooks-path'; const YourFormComponent = ({ someIdFromProps }) => { const [someBooleanState, setSomeBooleanState] = useState<boolean>(false); // 顶层调用自定义钩子,依赖someIdFromProps自动触发首次请求 const { data: things, refetch, // 手动触发请求的方法 isLoading, // 加载状态 error // 请求错误信息 } = useSomethingQuery(someIdFromProps);
2. 替代原useEffect逻辑
利用useEffect监听useQuery返回的data变化,自动更新someBooleanState,替代原来手动写的请求逻辑:
// 当things数据更新时,计算并更新状态 useEffect(() => { if (things) { const someBoolean = things.some(item => item.someProp === 'SOME_VALUE'); setSomeBooleanState(someBoolean); } }, [things]);
3. 改造校验器逻辑
校验器内不能调用钩子,但可以使用useQuery提供的refetch方法手动触发请求(复用缓存和请求逻辑),完成校验:
// 定义校验器函数 const uploadValidator = async () => { // 可选:如果缓存已有数据,直接用缓存;否则手动刷新 let targetThings = things; if (!targetThings) { const refetchResult = await refetch(); targetThings = refetchResult.data; } if (!targetThings) { // 请求失败的情况,直接返回错误 return Promise.reject(new Error('Failed to fetch necessary data')); } const someBoolean = targetThings.some(item => item.someProp === 'SOME_VALUE'); setSomeBooleanState(someBoolean); return someBoolean ? Promise.resolve() : Promise.reject(new Error('Please upload a file with certain thing')); };
4. 渲染表单组件
最后把校验器绑定到Form.Item的规则中:
return ( <Form> {/* 处理加载/错误状态(可选) */} {isLoading && <div>Loading...</div>} {error && <div>Error: {error.message}</div>} <Form.Item name="upload" label="Upload File" rules={[ { required: true, message: 'Please upload a file' }, { validator: uploadValidator } ]} > {/* 你的文件上传组件 */} </Form.Item> </Form> ); }; export default YourFormComponent;
关键说明
useQuery会自动在组件首次渲染、queryKey(这里是['something', someIdFromProps])变化时发起请求,无需手动在useEffect中写请求逻辑。refetch方法可以在任意回调函数中调用,既复用了useSomethingQuery的请求逻辑,又符合React钩子规则。- 如果校验时允许使用缓存数据,可以跳过
refetch直接用things,提升性能;如果必须获取最新数据,则调用refetch。
内容的提问来源于stack exchange,提问作者syydi
相关产品推荐
相关产品推荐

