Testing a hook with renderHook and nothing else

A hook cannot be called outside a component, so testing one used to mean building a throwaway component and asserting on what it rendered — which tests the markup rather than the hook.

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 a stale snapshot, which is the same closure problem appearing in a test. rerender with new arguments is how a dependency change is exercised, and it is the part most hook tests omit.