React函数组件Hook调用报错:无法在回调内调用useOptInOut
问题排查与解决:React Hook 不能在回调中调用的错误
错误原因
React Hook的核心规则明确要求:必须在函数组件的顶层作用域调用,不能嵌套在回调函数、条件判断、循环等代码块内部。你在onSubmit回调里调用useOptInOut,直接违反了这条规则,导致React无法保证Hook的调用顺序,进而触发报错。
另外你的代码还存在几个隐性问题:
- 未定义的变量
optType、setOptInOut,缺少选择状态的本地维护逻辑 - 单选框的显示逻辑依赖
enable,导致只能显示一个选项,不符合单选框的交互逻辑
解决方案步骤
- 调整Hook调用位置:将
useOptInOut移到组件顶层,自定义Hook应该返回一个用于发起请求的函数,而非直接在调用时执行请求 - 维护本地选择状态:用
useState记录用户选择的optType,配合单选框的checked属性实现正确的选中状态 - 在回调中调用请求函数:在
onSubmit里调用Hook返回的请求函数,传入选择的参数
修正后的完整代码
import { useState } from 'react'; import { useOptInOut } from "../../hooks/useOptInOut/useOptInOut"; import { useOptInOutStatus } from "../../hooks/useOptInOut/useOptInOutStatus"; import withTranslation, { WithTranslationComponentProps } from '../../hoc/WithTranslation'; const DataConsent = (props: WithTranslationComponentProps) => { const optStatusResponse = useOptInOutStatus(); const enable = optStatusResponse.value?.enabled; // 顶层调用自定义Hook,获取更新函数与加载状态 const { updateOptInOut, loading } = useOptInOut(); // 初始化选中状态:根据当前enable值设置默认选项 const [optType, setOptType] = useState<string>(enable ? 'optOut' : 'optIn'); const selectOptType = (e: React.ChangeEvent<HTMLInputElement>) => { setOptType(e.target.value); }; const onSubmit = async () => { // 避免重复提交 if (loading) return; try { // 调用Hook返回的请求函数发起POST请求 const response = await updateOptInOut(optType); // 这里可以添加提交成功后的逻辑,比如更新状态、提示用户 console.log('提交成功:', response); } catch (error) { // 处理请求失败的情况 console.error('提交失败:', error); } }; return ( <section className="app-section-container"> <div className="radio"> <label> <input type="radio" value="optIn" checked={optType === 'optIn'} onChange={selectOptType} disabled={loading} /> <b>Opt-in: </b> </label> <label> <input type="radio" value="optOut" checked={optType === 'optOut'} onChange={selectOptType} disabled={loading} /> <b>Opt-out: </b> </label> </div> <div className="cta-buttons"> <button className={`ui primary button ${loading ? 'loading' : ''}`} onClick={onSubmit} disabled={loading} > SUBMIT </button> </div> </section> ); }; export default withScrollTop(withTranslation(DataConsent as any));
自定义Hook useOptInOut 的参考实现
你的useOptInOut应该设计成返回请求函数的形式,示例如下:
// ../../hooks/useOptInOut/useOptInOut.ts import { useState } from 'react'; export const useOptInOut = () => { const [loading, setLoading] = useState(false); // 定义发起POST请求的函数 const updateOptInOut = async (optType: string) => { setLoading(true); try { // 替换成你的实际API地址 const response = await fetch('/api/user/consent', { method: 'POST', headers: { 'Content-Type': 'application/json', // 如果需要认证,添加token等头部信息 }, body: JSON.stringify({ enabled: optType === 'optIn' }) }); if (!response.ok) { throw new Error('请求失败'); } return await response.json(); } finally { setLoading(false); } }; return { updateOptInOut, loading }; };
内容的提问来源于stack exchange,提问作者Chandu
相关产品推荐
相关产品推荐

