PATTERN Cited by 1 source
Component-tree simplification¶
Intent¶
Flatten a component tree composed of many thin reusable wrappers into fewer dedicated, use-case-specific components. Pay some code duplication to win fewer render calls, fewer render layers, simpler data access, cheaper memoization.
Context¶
The default React (Vue, Angular) reaction to "this code could be shared" is to extract a wrapper component. At small scale this is fine. At hot-path scale (concepts/hot-path) — e.g. rendering 10,000 diff lines, 10,000 table rows, 10,000 list items — thin wrappers compound:
- Each wrapper is its own render call, reconcile pass, memo check.
- Each wrapper typically has a
useEffector two — scattered effects across the tree defeat memoization and drive extra renders (concepts/react-re-render). - Shared wrappers often carry logic for all variants even though only one variant is active, paying cost for unused code paths.
- Props + state flow through more layers; data access on the leaf gets awkward.
The abstraction is optimising for an axis (code reuse) that stopped mattering when the hot path's cost became dominant.
Mechanism¶
- Identify the hot-path component tree via rendering profile (React DevTools Profiler / flame graphs).
- Inline the wrappers into dedicated per-use-case components. Accept some code duplication. The rule "Don't Repeat Yourself" loses to "Don't Re-render Yourself" at scale.
- Narrow the surviving component's responsibility (Single- Responsibility Principle framing). A diff-line component should render a line. Commenting state, context-menu state, hover state should live elsewhere (patterns/conditional-child-state-scoping).
- Measure. Expect the render-count metric (React components rendered per interaction / frame) to drop dramatically — often much more than the DOM-node count.
Canonical instance¶
GitHub's Files-changed tab (2026-04-03) — v1 to v2 rewrite:
- v1: ≥8 components per unified diff line / ≥13 per split. Thin wrappers existed to share logic between split and unified views. Each wrapper carried logic for both views even though only one rendered at a time.
- v2: Dedicated per-view components. 2 components per diff line. Split and unified each have their own dedicated implementation. Some code duplication, "but the result is simpler and faster."
Reported impact on a 10,000-line split-diff benchmark:
- Unique component types: 19 → 10 (−47 %).
- Components rendered: ~183,504 → ~50,004 (−74 %).
- DOM nodes: ~200,000 → ~180,000 (only −10 % — the DOM wasn't where the cost lived).
- Memory: 150-250 MB → 80-120 MB (~−50 %).
- INP: ~450 ms → ~100 ms (~−78 %).
The DOM reduction was minor; the React-component reduction was where the win came from. This is the load-bearing lesson — at a certain scale, framework-runtime cost dominates over the DOM it produces.
Anti-patterns¶
- Flattening without measuring. If the wrappers aren't on the profiler's hot spots, don't touch them. DRY is still correct when there's no performance forcing function.
- Keeping
useEffectscattered after flattening. Flattening components without also consolidating effects under the top level leaves much of the re-render cost in place. The two changes are complementary, not alternative. - Over-flattening a component into one giant file. Dedicated per-use-case components aren't one-per-product; they're one-per-distinct-rendering-shape. Split view + unified view = 2 components is right; all diff-line variants in a 5,000-line file is wrong.
- Ignoring the linting layer. Without ESLint rules preventing
future
useEffectintroduction in the simplified components, the tree re-accretes wrappers over time (patterns/ci-regression-budget-gate at the hook level).
Related¶
- concepts/react-re-render — what this pattern attacks.
- concepts/hot-path — where this pattern pays off.
- patterns/conditional-child-state-scoping — the companion pattern for expensive state.
- patterns/single-top-level-event-handler — the companion pattern for event handlers.
- patterns/constant-time-state-map — companion pattern for interaction-time state lookups.
- systems/github-pull-requests — canonical wiki instance.
- systems/react — the framework this pattern targets.