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

useInterval调用子函数报React Hook与void返回类型错误排查

问题解答

首先你遇到的TypeError: savedCallback.current is not a function运行时错误,核心原因是第9行传给useInterval的第一个参数不是函数:你写的GetProjectScans(projectScans,isProjectScansFetchComplete)是立即执行的函数调用,执行后返回undefined,而useInterval内部会把传入的第一个参数存到savedCallback.current里,后续定时器触发时调用,传入undefined自然会报“不是函数”的错误。
另外你的代码还违反了React Hooks调用规则:useFetchProjectScansByUser是自定义Hook,只能在组件顶层或其他Hook顶层调用,不能在普通函数GetProjectScans内部调用。
针对你提出的三个具体问题,解答如下:

1. 是否需要为第4行的projectScans、isProjectScansFetchComplete显式声明变量类型?

不需要。
你已经为useFetchProjectScansByUser明确标注了返回值类型为[IProjectScan[], boolean],TypeScript会自动根据返回值类型推断解构出的两个变量的类型,额外显式声明属于冗余代码。
额外注意:你这里用let声明Hook返回的解构值是错误的,React的state是只读的,不能直接对解构出的state变量重新赋值,这也是你第6行代码的核心逻辑错误之一。

2. 为何TypeScript提示GetProjectScans内的入参变量未被使用?

两个原因:

  • 你在GetProjectScans内部没有读取这两个入参做任何逻辑判断、计算,仅在第6行尝试给这两个入参重新赋值,赋值后也没有在函数内使用这两个值,TS的未使用变量检查会直接提示入参声明后未被使用。
  • 退一步说,就算你给这两个入参赋值,改动也仅在GetProjectScans函数作用域内生效,根本修改不了组件顶层第4行解构出的state变量,完全达不到更新页面数据的效果。

3. 为何第9行的useInterval调用必须添加@ts-ignore才能通过类型校验?

因为类型不匹配。
useInterval的类型定义要求第一个参数必须是函数类型,但是你传入的是GetProjectScans(projectScans,isProjectScansFetchComplete)的执行结果——也就是undefined,和要求的函数类型完全不匹配,TS自然会抛出类型错误。加@ts-ignore只是强行跳过类型检查,并没有解决实际问题,这也是后续运行时报错的直接诱因。

正确修正方案

不要在普通函数内部调用Hook,应该把数据拉取逻辑作为方法从自定义Hook中暴露出来,再传入useInterval:
首先修改自定义Hook,暴露拉取数据的方法:

export const useFetchProjectScansByUser = (user: string|null): [IProjectScan[], boolean, () => Promise<void>] => {
    const [projectScans, setProjectScans] = useState<IProjectScan[]>([]);
    const [isProjectScansFetchComplete, setIsProjectScansFetchComplete] = useState<boolean>(false);
    const {enqueueSnackbar} = useSnackbar();

    // 把原来useEffect里的请求逻辑抽成独立方法
    const fetchScans = async () => {
        setIsProjectScansFetchComplete(false);
        try {
            // 此处写接口请求逻辑,拿到结果后调用setProjectScans更新数据
        } catch (e) {
            enqueueSnackbar('拉取扫描记录失败', {variant: 'error'});
        } finally {
            setIsProjectScansFetchComplete(true);
        }
    }
    
    // 组件首次加载、user变化时拉取一次数据
    useEffect(() => {
        fetchScans();
    }, [user])

    return [projectScans, isProjectScansFetchComplete, fetchScans];
}

然后修改页面组件的调用逻辑:

const ExecutionsPage: React.FC = () => {
    const user: string|null = sessionStorage.getItem('user')
    const UPDATE_TABLE_TIMER: number = 30000;
    // 用const声明state,不要用let
    const [projectScans, isProjectScansFetchComplete, fetchProjectScans] = useFetchProjectScansByUser(user)
    // 直接传入函数引用,不要加括号立即执行,也不需要@ts-ignore
    useInterval(fetchProjectScans, UPDATE_TABLE_TIMER)

    // 其余页面渲染逻辑
}

内容的提问来源于stack exchange,提问作者web-dev-nerd199

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 17:39:18