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

React出现useEffect/useState条件调用报错,useQuery设备数据状态赋值问题

问题原因分析

React Hook有强制调用规则:所有Hook必须在组件的最顶层调用,不能出现在条件判断、循环、嵌套函数、提前return的代码之后,保证每次组件渲染时Hook的调用顺序完全一致。你的代码违反了该规则,具体问题如下:

  • 初始代码里useEffect写在了loading、error两个提前return逻辑之后,一旦触发loading或者error状态,useEffect就不会执行,属于典型的条件调用Hook,因此抛出对应报错。
  • 表单变更回调HandleChange内部嵌套了useEffect,Hook不能在普通函数/事件回调中调用,只能在组件顶层或自定义Hook中调用,属于违规用法。
  • 你修改后的拆分组件方案,将deviceObject定义为全局变量、手动调用组件函数,完全不符合React组件渲染逻辑,不仅没有解决原有问题,还会引入数据不同步、内存泄漏等新问题。
  • 原有useEffect依赖数组为空,就算调整了调用位置,也只会在组件第一次挂载时执行,若dataRowId变化、查询结果更新,state不会同步更新,不符合业务需求。
解决方案

以下是修改后的完整可运行代码,完全符合你的业务需求,且无多余重渲染:

export default function DeviceModal(props) {
    const dataRowId = props.dataRowId;
    const classes = useStyles();
    // 初始化state直接用空设备结构,避免初始值类型不匹配
    const [row, setRow] = useState({
        id : 0,
        deviceId : 0,
        deviceClass :{name : '',},
        serialNumber: 0,
    });

    const device = useQuery(getDevice_query, {variables: {id: dataRowId}});

    // 所有Hook放在最顶部,条件return之前,符合调用规则
    useEffect(() => {
        // 仅当查询完成、无错误、有返回数据时才更新state,保证setRow仅在必要时调用
        if (!device.loading && !device.error && device.data) {
            const targetDevice = device.data.getDevice ?? {
                id : 0,
                deviceId : 0,
                deviceClass :{name : '',},
                serialNumber: 0,
            };
            setRow(targetDevice);
        }
    // 依赖项仅和查询状态相关,查询结果变化时才会重新执行
    }, [device.loading, device.error, device.data]);

    // 表单变更逻辑直接写在回调中,不需要套useEffect
    const HandleChange = e => {
        // 用函数式更新保证拿到最新的state值,避免闭包问题
        setRow(prev => ({...prev, [e.target.name]: e.target.value }));
    };

    const handleSave = (value) => {
        // 自行补充提交保存逻辑
    };

    // 条件return放在所有Hook之后
    if (device.loading) return <DataLoader/>;
    if (device.error) return <p style={{color: 'white'}}>{("GraphQL Error " + device.error)}</p>;

    return (
        <div>
            <Modal ...>
                <div className={classes.paper}>
                    <Grid container direction="row" justify="center"  alignItems="center">
                        <Grid item xs={4}>
                            <TextField
                                id="deviceId"
                                name="deviceId"
                                // 改用受控组件的value属性,保证输入和state完全同步
                                value={row.deviceId}
                                onChange={HandleChange}
                            />
                        </Grid>
                        {/* 其余30个TextField按相同规则配置value和onChange即可 */}
                        ....
                    </Grid>
                </div>
            </Modal>
        </div>
    )
};

内容的提问来源于stack exchange,提问作者Neal Rogers

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 12:27:03