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

CRA+React17项目ES6导入module.exports自定义钩子报错

问题本质与报错说明

这个问题不是IDE配置导致的,是CRA默认的Babel模块规则限制引发的ES模块(ESM)与CommonJS模块规范混用冲突,Next.js默认对两种模块规范做了兼容处理,所以你此前在Next.js项目中运行正常。

两个报错的具体含义:

  • 第一个报错Attempted import error: 'usePrevious' is not exported from '../customHooks.js':CRA默认将.js后缀的文件识别为ESM规范,ESM使用import/export语法处理导入导出,你在customHooks.js中用的module.exports是CommonJS的导出语法,ESM解析逻辑无法识别CommonJS的具名导出,因此抛出找不到对应导出内容的错误。
  • 第二个报错TypeError: Cannot assign to read only property 'exports' of object '#<Object>':当文件中同时出现ESM的import语法和CommonJS的module.exports语法时,Babel会将该文件判定为ESM规范,而ESM规范下exports是只读属性,不允许赋值,因此你改用require导入时会触发这个错误。
解决办法

方案1:统一改为ESM导出(最推荐,适配CRA默认规则)

无需修改导入逻辑,仅调整customHooks.js的导出写法即可,两种写法可选:

写法1:定义时直接导出

import { useCallback, useEffect, useRef, useState } from "react";

export const usePrevious = (stateValue) => {
    const ref = useRef();
    useEffect(() => {
        ref.current = stateValue;
    })
    return ref.current
}
export const getUrlVars = (url) => {
    let vars = {};
    if (url) {
        let parts = url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
            vars[key] = value;
        });
    } else {
        console.error('No url specified')
    }

    return vars;
}
export const setCookieManually = async (cname, cvalue, exdays) => {
    const d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    let expires = "expires="+ d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
export const removeCookieManually = async (cname) => {
    document.cookie = `${cname}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
}
export const useSetState = (initialValue) => {
    let [val, setVal] = useState(initialValue);
    let cbRef = useRef(null);
    let first = useRef(true);

    useEffect(() => {
        if (first.current) {
            first.current = false;
            return;
        }

        if (typeof cbRef.current === "function") {
            console.log("calling cb");
            cbRef.current();
        }
    }, [val]);

    let setValCB = useCallback((newVal, cb) => {
        console.log("setValCB", newVal);
        cbRef.current = cb;
        setVal(newVal);
    }, []);

    return [val, setValCB];
}

写法2:定义完成后统一导出

import { useCallback, useEffect, useRef, useState } from "react";

const usePrevious = (stateValue) => {
    const ref = useRef();
    useEffect(() => {
        ref.current = stateValue;
    })
    return ref.current
}
const getUrlVars = (url) => {
    let vars = {};
    if (url) {
        let parts = url.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
            vars[key] = value;
        });
    } else {
        console.error('No url specified')
    }

    return vars;
}
const setCookieManually = async (cname, cvalue, exdays) => {
    const d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    let expires = "expires="+ d.toUTCString();
    document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
}
const removeCookieManually = async (cname) => {
    document.cookie = `${cname}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`
}
const useSetState = (initialValue) => {
    let [val, setVal] = useState(initialValue);
    let cbRef = useRef(null);
    let first = useRef(true);

    useEffect(() => {
        if (first.current) {
            first.current = false;
            return;
        }

        if (typeof cbRef.current === "function") {
            console.log("calling cb");
            cbRef.current();
        }
    }, [val]);

    let setValCB = useCallback((newVal, cb) => {
        console.log("setValCB", newVal);
        cbRef.current = cb;
        setVal(newVal);
    }, []);

    return [val, setValCB];
}

// 统一导出所有方法
export {
    usePrevious,
    getUrlVars,
    setCookieManually,
    removeCookieManually,
    useSetState
}

改完后原有import { usePrevious } from '../customHooks'的导入逻辑无需修改,可直接运行。

方案2:强制使用CommonJS规范

如果不想修改原有导出代码,可以将customHooks.js的后缀改为.cjs,CRA会自动识别.cjs文件为CommonJS模块,导入时改用require语法即可:

const { usePrevious } = require('../customHooks.cjs')

方案3:修改Babel配置兼容混用(不推荐)

需要eject CRA配置或者通过craco等工具修改Babel配置,添加sourceType: 'unambiguous'参数让Babel自动判断模块类型,该方案修改成本高,容易引发其他依赖的模块冲突,不推荐使用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 18:27:02