Adds two dev-only counters that attribute re-renders and store notifications during an interaction, so a perf claim can be answered with a number instead of a hunch: window.__RENDER_COUNTS__ what re-rendered, and why (props/state/parent) window.__ATOM_CHURN__ which store published it, and whether it mattered The `wasted` column in each is the fix list — components that re-rendered with no changed input, and stores that published a value equal to the last one. Both are inert until start(), so idle cost is one branch per commit and per notify. React 19.2 removed injectProfilingHooks from react-dom, so the mark* profiling family is unavailable and onCommitFiberRoot is the only channel left. <Profiler> can't answer the question either: React invokes onRender for every Profiler in a committed tree including subtrees that bailed out, and a bailed-out subtree still reports nonzero actualDuration. This uses bippy's didFiberRender instead. bippy over react-scan because react-scan/lite is a thin wrapper over it while the package pulls ~217 transitive deps and floats two on latest. main.tsx imports the entry statically above react-dom because react-dom captures the devtools hook at module init — a late install reports renderers=0 and observes zero commits. Production exclusion is handled by a build-time alias to a no-op module rather than tree-shaking, since a static side-effect import can't be eliminated.
20 lines
928 B
TypeScript
20 lines
928 B
TypeScript
// Dev-only diagnostics entry. Statically imported from `main.tsx` ABOVE the
|
|
// `react-dom` import — that ordering is load-bearing and non-negotiable.
|
|
//
|
|
// react-dom captures the devtools hook at MODULE INIT, not at `createRoot`.
|
|
// Verified: installing bippy after react-dom has evaluated yields
|
|
// `renderers=0` and `commits=0` — the hook object exists but react-dom never
|
|
// registered with it. A dynamic `import()` here would resolve a microtask
|
|
// after main.tsx's static graph (react-dom included) had already evaluated,
|
|
// so it must be a plain static import chain, whose evaluation order ESM
|
|
// guarantees.
|
|
//
|
|
// Production builds don't tree-shake this away (a static side-effect import
|
|
// can't be eliminated) — instead `vite.config.ts` aliases this module to
|
|
// `dev-only.noop.ts` whenever the build isn't serving dev, so neither bippy
|
|
// nor the counters reach a shipped renderer.
|
|
|
|
import './index'
|
|
|
|
export {}
|