如何在Jest中测试基于Recoil的自定义Hook?RecoilRoot位置存疑
解决Recoil自定义Hook测试中"This component must be used inside a component"错误
你遇到的问题核心是调用Recoil Hook的代码没有处于RecoilRoot的上下文范围内。在你的测试代码里,useIds是在TestComponent的顶层直接调用的,而RecoilRoot是在TestComponent的JSX里才渲染的——组件执行顺序是先跑顶层的Hook逻辑,再渲染JSX,所以当useRecoilState执行时,Recoil的上下文还没建立,自然会报错。
下面给你两种直接可行的修复方案:
方案一:在render时用RecoilRoot包裹测试组件
把RecoilRoot提到测试组件外面,让整个测试组件都处于Recoil上下文里:
import * as React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { useIds } from '@/hooks/useIds'; import { RecoilRoot } from 'recoil'; it('unit test for custom hook useIds', () => { // 测试组件直接调用useIds,不需要自己嵌套RecoilRoot const TestComponent: React.FC = () => { const ids = useIds(); return <div title='ids'>{ ids }</div> } // 渲染时用RecoilRoot包裹测试组件 const { getByTitle } = render( <RecoilRoot> <TestComponent /> </RecoilRoot> ); const idsElement = getByTitle('ids'); // 这里可以添加你的断言逻辑,比如expect(idsElement.textContent).toBe(...) })
方案二:在测试组件内部的子组件中调用useIds
如果一定要在测试组件里嵌套RecoilRoot,那需要把useIds的调用放到RecoilRoot的子组件中,确保Hook执行时上下文已经存在:
it('unit test for custom hook useIds', () => { const TestComponent: React.FC = () => { return ( <RecoilRoot> {/* 子组件内部调用useIds,此时已经处于Recoil上下文 */} <InnerComponent /> </RecoilRoot> ) } const InnerComponent: React.FC = () => { const ids = useIds(); return <div title='ids'>{ ids }</div> } const { getByTitle } = render(<TestComponent />); const idsElement = getByTitle('ids'); // 添加断言逻辑 })
优化建议:封装带RecoilRoot的自定义render函数
如果你的测试中有很多组件需要用到Recoil,可以封装一个自定义render函数,避免重复写RecoilRoot:
import { render } from '@testing-library/react'; import { RecoilRoot } from 'recoil'; const renderWithRecoil = (ui: React.ReactElement) => { return render(<RecoilRoot>{ui}</RecoilRoot>); }; // 在测试中使用 it('unit test for custom hook useIds', () => { const TestComponent: React.FC = () => { const ids = useIds(); return <div title='ids'>{ ids }</div> } const { getByTitle } = renderWithRecoil(<TestComponent />); // ...断言逻辑 })
本质上,所有调用Recoil Hooks(比如useRecoilState、useRecoilValue)的代码,不管是直接在组件里还是在自定义Hook里,都必须位于RecoilRoot的子组件树中,这样才能获取到Recoil的状态上下文。
内容的提问来源于stack exchange,提问作者Satoru Kikuchi
相关产品推荐
相关产品推荐

