Skip to content

Instantly share code, notes, and snippets.

@drbr
Last active November 17, 2021 01:50
Show Gist options
  • Save drbr/a471920a76da551266ff5a764c7ff41d to your computer and use it in GitHub Desktop.
Save drbr/a471920a76da551266ff5a764c7ff41d to your computer and use it in GitHub Desktop.
Unit tests that demonstrate how `useMemo` in a parent is equivalent to `React.memo` on a child component
import { cleanup, render, screen } from '@testing-library/react';
import * as React from 'react';
type MemoOption = 'none' | 'hook' | 'HOC';
function ParentComponent({ log, memo }: { log: (msg: string) => void; memo: MemoOption }) {
log('parent');
// This value is not sent into the child, it's just used to rerender the parent.
const [value, setValue] = React.useState(0);
const memoizedChildWithHOC = <ChildMemo value="foo" log={log} />;
const memoizedChildWithHook = React.useMemo(() => <ChildComponent value="foo" log={log} />, [
log,
]);
const plainChild = <ChildComponent value="foo" log={log} />;
let child: JSX.Element = <></>;
switch (memo) {
case 'HOC':
child = memoizedChildWithHOC;
break;
case 'hook':
child = memoizedChildWithHook;
break;
case 'none':
child = plainChild;
break;
}
return (
<div>
<div>{value}</div>
{child}
<button onClick={() => setValue(v => v + 1)}>Increment</button>
</div>
);
}
ParentComponent.displayName = 'ParentComponent';
function ChildComponent({ value, log }: { value: string | number; log: (msg: string) => void }) {
log('child');
return <div>{value}</div>;
}
ChildComponent.displayName = 'ChildComponent';
const ChildMemo = React.memo(ChildComponent);
describe('How does memoization of JSX work?', () => {
function renderIt(opts: { memo: MemoOption }) {
const ledger = [];
const log = ledger.push.bind(ledger);
render(<ParentComponent log={log} memo={opts.memo} />);
return { ledger };
}
async function getButton() {
return await screen.findByText('Increment');
}
afterEach(() => cleanup());
it('rerenders the child when the child component is not memoized', async () => {
const { ledger } = renderIt({ memo: 'none' });
(await getButton()).click();
expect(ledger).toEqual(['parent', 'child', 'parent', 'child']);
});
it('does not rerender the child when memoized with the hook', async () => {
const { ledger } = renderIt({ memo: 'hook' });
(await getButton()).click();
expect(ledger).toEqual(['parent', 'child', 'parent']);
});
it('does not rerender the child when memoized with the HOC', async () => {
const { ledger } = renderIt({ memo: 'HOC' });
(await getButton()).click();
expect(ledger).toEqual(['parent', 'child', 'parent']);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment