Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock a react function component that takes a ref prop?

The Problem:

I am trying to use jest and React Testing Library to mock a functional component that is wrapped in React.ForwardRef(), but I keep getting this warning (which is failing my test):

Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?

Here is the component I want to test:

const ParentComponent = () => {
  const childRef = useRef(null);

  return (
    <div data-testid="parent">
      Parent
      {/* want to mock this ChildComponent */}
      <ChildComponent ref={childRef} />
    </div>
  );
};

Here is the component I want to mock:

const ChildComponent = forwardRef((props, ref) => (
  <div ref={ref} {...props}>
    Child
  </div>
));

What I've tried:


jest.mock("../ChildComponent", () => ({
  __esModule: true,
  default: () => <div>Mock Child</div>
}));

result: Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?


jest.mock('../ChildComponent', () => {
  const { forwardRef } = jest.requireActual('react');
  return {
    __esModule: true,
    default: () => forwardRef((props, ref) => <div ref={ref} />),
  };
});

result: Objects are not valid as a React child (found: object with keys {$$typeof, render})

like image 653
Charlie Maloney Avatar asked Dec 14 '25 11:12

Charlie Maloney


2 Answers

Sweet, I just had this exact same issue and your last attempt where you imported forwardRef within the mock function helped me figure this out. I had luck with this:

jest.mock('../../../client/components/visualizations/Visualizer', () => {
  const { forwardRef } = jest.requireActual('react');
  return {
    __esModule: true,
    default: forwardRef(() => <div></div>),
  };
});

So using your example from above, I believe this should work:

jest.mock('../ChildComponent', () => {
  const { forwardRef } = jest.requireActual('react');
  return {
    __esModule: true,
-   default: () => forwardRef((props, ref) => <div ref={ref} />),
+   default: forwardRef((props, ref) => <div ref={ref} />),
  };
});

Also including my own attempt below with the resulting error message in case it helps someone else find this post in the future:

jest.mock('../ChildComponent', () => ({
  __esModule: true,
  default: React.forwardRef(() => <div></div>),
}));

Resulted in error:

The module factory of `jest.mock()` is not allowed to reference any out-of-scope variables.
    Invalid variable access: React`
like image 98
Ryan S Avatar answered Dec 17 '25 01:12

Ryan S


I had a similar issue to Ryan S, and used their answer with a slight modification.

In my problem, I had to test with enzyme that the exact child component was rendered, but without using enzyme's mount only shallow:

it('renders the child', () => {
  expect(wrapper.exists(ChildComponent)).toBe(true);
})

My modification of the solution is thus:

jest.mock('../ChildComponent', () => {
  const { forwardRef } = jest.requireActual('react');
  const ChildComponentShim = jest.requireActual('../ChildComponent');
  return {
    __esModule: true,
    default: forwardRef((props, ref) => <ChildComponentShim { ...props } ref={ref} />),
  };
});

Which is merely to let the actual child component to be rendered, but makes sure the ref from forwardRef is used without mounting the parent or child component.

like image 25
AncientSwordRage Avatar answered Dec 17 '25 01:12

AncientSwordRage



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!