Skip to content

Stop factsheet charts redrawing on every table render - #313

Open
LunaUrsa wants to merge 1 commit into
uatfrom
fix/289-factsheet-expand-perf
Open

LunaUrsa wants to merge 1 commit into
uatfrom
fix/289-factsheet-expand-perf

Conversation

@LunaUrsa

Copy link
Copy Markdown
Contributor

Refs #289

What this changes

  • Only expanded rows build a factsheet card. Material React Table calls renderDetailPanel for every row on the page on every render, expanded or not, and only mounts the result when the row is open. Factsheets.tsx called DrugInfoCard({ drugData }) as a plain function there, so every row's card was computed on each table render. It now returns <DrugInfoCard drugData={row.original} />.
  • An open card no longer re-renders with the table. DrugInfoCard is wrapped in memo. Without it, every table render (each keystroke in a column filter, for example) re-rendered the open card. react-apexcharts deep-compares its options prop, and the inline tooltip.custom functions never compare equal, so both charts were redrawn from scratch each time.
  • Charts no longer animate in. ApexCharts' grow-in animation kept the browser drawing frames for about two seconds after each expansion. The charts are now drawn once.

Measurements

Production build, headless Chrome 148 via puppeteer, 1600×1000, expanding the LSD row. Baseline is uat at 7724008.

Before After
Typing an 11-letter filter with LSD expanded: DOM mutations inside the charts 968 0
Same: main-thread JS ~360 ms ~160 ms
First 5 s after expanding: frames committed 112 32
Same: paints 249 99
Same: animation-frame callbacks 343 0

The cost of the expansion itself didn't change: about 0.3 s of JS, or about 1.8 s with 4× CPU throttling (mostly drawing the two charts plus the MUI mount).

Not reproduced

The 30–60 s busy renderer described in #289 did not reproduce. I tried headless Chrome, headful Chrome under WSLg, the live tripsit.io, several heavy drugs (LSD, pregabalin, diphenhydramine, ketamine), widths from 800 to 1920 px, DPR 1–2, 4× CPU throttling, 100 rows per page, expand-all, and 16 expand/collapse cycles. In each case the page went fully idle once the expansion settled (no commits, paints or running animations), and heap, window listeners and ApexCharts instances stayed flat across cycles. So this PR removes the measurable waste around an expansion, but I can't confirm it removes the original symptom. That's why it says "Refs" rather than "Closes". It would be worth re-checking in a desktop Chrome with a GPU before closing #289.

Tests

  • FactsheetsDetailPanel.test.tsx: with two collapsed rows, the card component is never called. After expanding one, it is called only for that row. Fails on uat (8 calls before any expansion).
  • DrugInfo.test.tsx: an open card draws its two charts once, with animations disabled, and re-rendering it with the same drug does not render the charts again. Fails on uat and fails separately if only the memo is removed.
  • npx eslint ., npm test -- --coverage (110 tests, branch coverage 90.28%), npx next build all pass.

git merge-tree shows no conflict with #308, which also edits DrugInfo.tsx.

🤖 Generated with Claude Code

The factsheets table calls renderDetailPanel for every row on each render,
and DrugInfoCard was called there as a plain function, so every row's card
was computed each time, collapsed or not. Render it as an element instead,
so only expanded rows build one.

Memoise DrugInfoCard so table re-renders (each filter keystroke, for
example) no longer re-render an open card. react-apexcharts compares its
options deeply, and the inline tooltip functions never compare equal, so
each re-render redrew both charts from scratch.

Turn off the charts' grow-in animation, which kept the browser drawing
frames for about two seconds after a row was expanded.

Refs #289

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant