Testing a hook without rendering a component

A hook cannot be called outside a component, so testing one used to mean building a throwaway component and asserting on what it rendered.

import { renderHook, act } from '@testing-library/react-hooks';

test('useCounter increments', () => {
  const { result } = renderHook(() => useCounter(0));

  act(() => result.current.increment());

  expect(result.current.count).toBe(1);
});

act is what flushes the state update and the effects; without it React warns and the assertion runs against the value before the update. result.current has to be read after the act rather than destructured before it, because the object is replaced on each render — destructuring gives you a stale snapshot, which is the same closure problem in a test. Testing the hook directly rather than through a component is what makes the test about behaviour instead of about markup.