How to Mock React Keys in React
In React, the key prop is a special attribute used by
the reconciliation algorithm to identify which items in a list have
changed, been added, or been removed. Because React consumes the
key prop internally, it is not passed down to the
component’s actual props, making it tricky to mock or assert on
directly. This article explains how to test that keys are correctly
assigned to React elements and how to simulate key changes to test
component re-mounting behavior using Jest and React Testing Library.
Why You Cannot Read
props.key
React reserves the key prop for its virtual DOM diffing
process. If you try to access props.key inside a child
component, it will return undefined.
To mock or test keys, you must inspect the React elements before they are fully rendered to the DOM, or wrap your components in a way that allows you to trigger key changes.
Asserting That Correct Keys Are Assigned
To verify that your list items are receiving the correct keys, you
can inspect the virtual DOM structure using
react-test-renderer or by directly checking the returned
React elements.
Here is an example using react-test-renderer to assert
that keys are correctly assigned to list items:
import React from 'react';
import TestRenderer from 'react-test-renderer';
function TodoList({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}
test('should assign correct keys to list items', () => {
const items = [{ id: 'todo-1', text: 'Learn React' }, { id: 'todo-2', text: 'Write Tests' }];
const renderer = TestRenderer.create(<TodoList items={items} />);
const root = renderer.root;
const listItems = root.findAllByType('li');
// Assert that the keys match the item IDs
expect(listItems[0].key).toBe('todo-1');
expect(listItems[1].key).toBe('todo-2');
});Simulating Key Changes to Force Re-mounting
Sometimes you need to test if a component resets its internal state
when its key changes. Since React completely unmounts and
remounts a component when its key changes, you can test this behavior in
React Testing Library by rerendering the component with a new key.
Here is how you can mock and test this behavior:
import React, { useState } from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<span data-testid="count">{count}</span>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
test('should reset state when the key prop changes', () => {
// 1. Render the component with an initial key
const { rerender } = render(<Counter key="initial-key" />);
const incrementButton = screen.getByText('Increment');
const countSpan = screen.getByTestId('count');
// 2. Increment the counter to change the internal state
fireEvent.click(incrementButton);
expect(countSpan.textContent).toBe('1');
// 3. Rerender the component with a different key
rerender(<Counter key="new-key" />);
// 4. Assert that the component unmounted, remounted, and state was reset
expect(countSpan.textContent).toBe('0');
});By inspecting the element tree with a test renderer or utilizing
React Testing Library’s rerender method to change keys
dynamically, you can effectively mock and verify key-dependent behavior
in your React applications.