You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React函数组件Hook调用报错:无法在回调内调用useOptInOut

问题排查与解决:React Hook 不能在回调中调用的错误

错误原因

React Hook的核心规则明确要求:必须在函数组件的顶层作用域调用,不能嵌套在回调函数、条件判断、循环等代码块内部。你在onSubmit回调里调用useOptInOut,直接违反了这条规则,导致React无法保证Hook的调用顺序,进而触发报错。

另外你的代码还存在几个隐性问题:

  • 未定义的变量optType、setOptInOut,缺少选择状态的本地维护逻辑
  • 单选框的显示逻辑依赖enable,导致只能显示一个选项,不符合单选框的交互逻辑

解决方案步骤

  1. 调整Hook调用位置:将useOptInOut移到组件顶层,自定义Hook应该返回一个用于发起请求的函数,而非直接在调用时执行请求
  2. 维护本地选择状态:用useState记录用户选择的optType,配合单选框的checked属性实现正确的选中状态
  3. 在回调中调用请求函数:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 19:27:22