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

React 18+React Hook Form RTL测试中unsubscribe未定义错误排查

问题:React 18.3.1 + React Hook Form 7.54.1 RTL测试报错:subscription.unsubscribe()不存在

错误信息

42 |                      setSomeState(newValue); // changed to dummy for now
      43 |              })
    > 44 |              return () => subscription.unsubscribe()
         |                                        ^
      45 |      }, [watch])
      46 |      return {
      47 |              nameCount,

      at unsubscribe (src/pages/TestFileName.tsx:44:29)
      at safelyCallDestroy (node_modules/react-dom/cjs/react-dom.development.js:3922:844)
      at commitHookEffectListUnmount (node_modules/react-dom/cjs/react-dom.development.js:3929:331)
      at commitPassiveUnmountInsideDeletedTreeOnFiber (node_modules/react-dom/cjs/react-dom.development.js:4161:525)
      at commitPassiveUnmountEffectsInsideOfDeletedTree_begin (node_modules/react-dom/cjs/react-dom.development.js:4156:24)
      at commitPassiveUnmountEffects_begin (node_modules/react-dom/cjs/react-dom.development.js:4143:2054)
      at commitPassiveUnmountEffects (node_modules/react-dom/cjs/react-dom.development.js:4143:1714)
      at flushPassiveEffectsImpl (node_modules/react-dom/cjs/react-dom.development.js:4640:360)
      at flushPassiveEffects (node_modules/react-dom/cjs/react-dom.development.js:4635:371)
      at commitRootImpl (node_modules/react-dom/cjs/react-dom.development.js:4625:82)
      at commitRoot (node_modules/react-dom/cjs/react-dom.development.js:4549:209)
      at performSyncWorkOnRoot (node_modules/react-dom/cjs/react-dom.development.js:4444:97)
      at flushSyncCallbacks (node_modules/react-dom/cjs/react-dom.development.js:2155:108)
      at flushSync (node_modules/react-dom/cjs/react-dom.development.js:4457:66)
      at ReactDOMRoot.Object.<anonymous>.ReactDOMHydrationRoot.unmount.ReactDOMRoot.unmount [as unmount] (node_modules/react-dom/cjs/react-dom.development.js:4917:1902)
      at Object.unmount (node_modules/@testing-library/react/dist/pure.js:178:12)
      at node_modules/@testing-library/react/dist/pure.js:309:12
      at node_modules/@testing-library/react/dist/act-compat.js:71:24
      at act (node_modules/react/cjs/react.development.js:2194:20)
      at node_modules/@testing-library/react/dist/act-compat.js:70:25
      at node_modules/@testing-library/react/dist/pure.js:308:28
      at node_modules/@testing-library/react/dist/act-compat.js:70:25
      at node_modules/@testing-library/react/dist/pure.js:308:28
      at node_modules/@testing-library/react/dist/pure.js:308:28
          at Array.forEach (<anonymous>)
      at cleanup (node_modules/@testing-library/react/dist/pure.js:304:22)
      at Object.<anonymous> (src/pages/TestFileName.test.tsx:78:10)

相关代码片段

TestFileName.tsx

const [nameCount, setNameCount] = useState(someStateData?.name?.length || 0)

useEffect(() => {
        const subscription = watch((value) => {
            setNameCount(value.name.length);
        })
        return () => subscription.unsubscribe(); // 测试中报错的行
    }, [watch])
    return {
        nameCount
    }
}

ParentContainer.tsx

const {
        register,
        handleSubmit,....// other methods
    } = useForm({
        defaultValues: {
            firstName: someDefaultValue,
        },... // other attributes
});

return (
    <>
        <TestFileName
            register={register}
            watch={watch}
            setValue={setValue}
            setFocus={setFocus} />
    </>
)

测试用例默认props

const defaultProps = {
    register: jest.fn(),
    watch: jest.fn(),
    setError: jest.fn()
};

问题原因

测试用例里的watch是一个空的jest mock函数,而真实的React Hook Form watch函数调用后会返回一个带有unsubscribe方法的订阅对象。你的mockwatch没有返回这个对象,导致组件卸载执行清理函数时,subscription要么是undefined,要么没有unsubscribe方法,从而抛出错误。

解决方案

调整测试用例中的watch mock,让它返回包含unsubscribe方法的对象:

const defaultProps = {
    register: jest.fn(),
    watch: jest.fn(() => ({
        unsubscribe: jest.fn()
    })),
    setError: jest.fn()
};

如果需要模拟watch的回调逻辑(比如测试状态更新),可以进一步完善mock:

const watchMock = jest.fn((callback) => {
    // 可在这里触发回调模拟表单值变化
    // callback({ name: '测试名称' });
    return {
        unsubscribe: jest.fn()
    };
});

const defaultProps = {
    register: jest.fn(),
    watch: watchMock,
    setError: jest.fn()
};

内容的提问来源于stack exchange,提问作者Nikhil Singh Bhadoriya

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:32:01