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

如何在useMemo中使用useComputed?ESLint报错问题求助

问题与解决:ESLint关于useCallback依赖与useComputed的报错

问题描述

ESLint提示:

The 'fields' object makes the dependencies of useCallback Hook (at line 183) change on every render. Move it inside the useCallback callback. Alternatively, wrap the initialization of 'fields' in its own useMemo() Hook

相关fields定义代码:

const fields: { [p: string]: EntityField } = {
        'Kredieten': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.kredieten.id), [metadataService]),
        'Deelnemingen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.deelnemingen.id), [metadataService]),
        'Toeleveranciers': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.toeleveranciers.id), [metadataService]),
        'Vervoersmiddelen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.vervoersmiddelen.id), [metadataService]),
        'Liquide middelen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.liquideMiddelen.id), [metadataService]),
        'Voorraden': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.voorraden.id), [metadataService]),
        'Groepsvermogen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.groepsvermogen.id), [metadataService]),
        'Gebouwen en terreinen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.gebouwenEnTerreinen.id), [metadataService]),
        'Machines & Installaties': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.machinesAndInstallaties.id), [metadataService]),
        'Inventaris': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.inventaris.id), [metadataService]),
        'Effecten': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.effecten.id), [metadataService]),
        'Rechten & Vergunningen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.rechtenAndVergunningen.id), [metadataService]),
    };

handleSave函数代码:

const handleSave = useCallback(async () =>
    {
        setLoading(true);

        Object.keys(data).forEach((x: string) => {
            mutationHandler.updateEntityWithProperty(
                dossier.entity,
                new FieldProperty(fields[x]),
                DataObjectValue.create(
                    new DataObject(
                        fields[x].specification,
                        {boolean: data[x]},
                    )
                ),
            );
        })

        try
        {
            await mutationHandler.commit();

            setLoading(false);
            enqueueSnackbar('Succesvol opgeslagen');
        }
        catch (e)
        {
            showError(e, 'Opslaan mislukt');

            setLoading(false);
        }
    }, [data, mutationHandler, dossier.entity, fields, enqueueSnackbar, showError]);

尝试用useMemo包裹fields(const fields: { [p: string]: EntityField } = useMemo(() => ({)后,ESLint对useComputed报错,该代码用于实现复选框逻辑。

原因分析

React Hook规则强制要求所有Hook必须在组件顶层作用域调用,不能嵌套在其他Hook(如useMemo的回调函数)内部。你将useComputed(遵循Hook规则的自定义Hook)放在useMemo回调里,违反了Hook调用规则,因此触发ESLint报错。

原提示的核心问题是fields对象每次渲染都会重新创建,导致useCallback依赖频繁变化,但直接用useMemo包裹整个fields的方式不符合Hook规则。

解决方案

方案一:将fields移到useCallback内部

把fields的定义直接放到useCallback回调中,既避免fields作为依赖导致的频繁更新,又不违反Hook规则:

const handleSave = useCallback(async () => {
    setLoading(true);
    
    // 将fields定义移到useCallback内部
    const fields: { [p: string]: EntityField } = {
        'Kredieten': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.kredieten.id), [metadataService]),
        'Deelnemingen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.deelnemingen.id), [metadataService]),
        // 其他字段保持不变
    };

    Object.keys(data).forEach((x: string) => {
        mutationHandler.updateEntityWithProperty(
            dossier.entity,
            new FieldProperty(fields[x]),
            DataObjectValue.create(
                new DataObject(
                    fields[x].specification,
                    {boolean: data[x]},
                )
            ),
        );
    })

    try {
        await mutationHandler.commit();
        setLoading(false);
        enqueueSnackbar('Succesvol opgeslagen');
    } catch (e) {
        showError(e, 'Opslaan mislukt');
        setLoading(false);
    }
}, [data, mutationHandler, dossier.entity, metadataService, enqueueSnackbar, showError]);

注意:此时useCallback的依赖需添加metadataService,因为内部useComputed依赖它。

方案二:封装自定义Hook管理fields

如果多个地方需要用到fields,可将其封装为自定义Hook,既符合Hook规则,又能保证fields引用稳定:

// 自定义Hook封装fields逻辑
const useEntityFields = () => {
    const fields = {
        'Kredieten': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.kredieten.id), [metadataService]),
        'Deelnemingen': useComputed(() => metadataService.getEntityField(RisicoAdviesDossier.PROPS.deelnemingen.id), [metadataService]),
        // 其他字段保持不变
    };
    return fields;
};

// 组件内使用
const fields = useEntityFields();

const handleSave = useCallback(async () => {
    setLoading(true);

    Object.keys(data).forEach((x: string) => {
        mutationHandler.updateEntityWithProperty(
            dossier.entity,
            new FieldProperty(fields[x]),
            DataObjectValue.create(
                new DataObject(
                    fields[x].specification,
                    {boolean: data[x]},
                )
            ),
        );
    })

    try {
        await mutationHandler.commit();
        setLoading(false);
        enqueueSnackbar('Succesvol opgeslagen');
    } catch (e) {
        showError(e, 'Opslaan mislukt');
        setLoading(false);
    }
}, [data, mutationHandler, dossier.entity, fields, enqueueSnackbar, showError]);

自定义Hook的返回值在依赖不变时会保持引用稳定,可安全作为useCallback的依赖。


内容的提问来源于stack exchange,提问作者Michał Różga

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:03:35