How to Mock useRef Hook in React
Mocking the useRef hook in React is a common requirement
when unit testing components that interact directly with the DOM or hold
mutable values. This article provides a straightforward guide on how to
mock useRef using Jest and React Testing Library,
demonstrating how to spy on the hook, control its .current
property, and verify component behavior during testing.
Why Mock useRef?
In React, useRef is typically used to access DOM
elements directly (like focusing an input, playing media, or calculating
element dimensions) or to persist values across renders without
triggering a re-render.
When writing unit tests, you might not want to render the full DOM or
rely on actual browser APIs. Mocking useRef allows you to
isolate the component’s logic by replacing the ref’s behavior with Jest
mock functions.
The Component to Test
Consider a simple component that uses useRef to
programmatically focus a text input when a button is clicked:
import React, { useRef } from 'react';
const FocusInput = () => {
const inputRef = useRef(null);
const handleFocus = () => {
if (inputRef.current) {
inputRef.current.focus();
}
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
};
export default FocusInput;How to Mock useRef Using jest.spyOn
The cleanest way to mock useRef is by using
jest.spyOn on the React object. This allows
you to override the default implementation of the hook and return a
custom mock object for the duration of your test.
Here is how you can write the test:
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import FocusInput from './FocusInput';
describe('FocusInput Component', () => {
it('should call the focus method on the input when the button is clicked', () => {
// 1. Create a mock focus function
const mockFocus = jest.fn();
// 2. Spy on React.useRef and mock its return value
jest.spyOn(React, 'useRef').mockReturnValue({
current: {
focus: mockFocus,
},
});
// 3. Render the component
render(<FocusInput />);
// 4. Trigger the click event
const button = screen.getByRole('button', { name: /focus input/i });
fireEvent.click(button);
// 5. Assert that the mocked focus function was called
expect(mockFocus).toHaveBeenCalledTimes(1);
});
});Explanation of the Steps
- Create a Mock Function: We define
mockFocus = jest.fn()to track whether the element’sfocusmethod is successfully called. - Spy on React.useRef: By using
jest.spyOn(React, 'useRef'), we intercept React’s internaluseRefimplementation. We chain.mockReturnValue()to return a dummy object containing our mock function inside thecurrentproperty. - Render and Act: We render the component using React Testing Library and simulate a user clicking the button.
- Assert: Finally, we assert that
mockFocuswas called, verifying that the ref integration works as expected.
Clean Up After Tests
Because jest.spyOn modifies the behavior of the
React object globally for the test file, it is important to
restore the original implementation after your tests run. You can
achieve this by adding jest.restoreAllMocks() in an
afterEach block:
afterEach(() => {
jest.restoreAllMocks();
});