perf: memoize IssueCard to avoid unnecessary rerenders#37
Conversation
There was a problem hiding this comment.
Code Review
This pull request wraps the IssueCard component in React.memo and replaces several CSS-based action badges with raw emojis. The feedback points out that replacing these CSS badges with raw emojis (🔍, 💡, 📚) bypasses the custom Badge & Indicator System defined in panel.css, which ensures visual consistency across platforms. Additionally, changing the class name from learn-link to learn-more-link breaks the link's styling because the new class is not defined, and introduces inconsistent indentation. The reviewer has provided suggestions to restore the original CSS badges and class names.
|
@ELITE-DEV-22 Please check comment from Gemini |
hoainho
left a comment
There was a problem hiding this comment.
Thanks for picking this up @ELITE-DEV-22. The React.memo wrap itself is mechanically correct — but the PR also includes several changes that revert intentional work from v2.0.0 and break CSS classes. Requesting changes for those; the memo wrap can stay.
⛔ Critical — must fix before merge
1. Reverts v2.0.0 emoji → CSS-icon migration
IssueCard.tsx:161 changes <span className="action-badge action-badge--search" /> back to 🔍. IssueCard.tsx:222 changes <span className="action-badge action-badge--suggestion" /> back to 💡. The 📚 Learn more change on line ~234 similarly reverts action-badge--learn.
Commit eb14410 (Feb 22, 2026, "major extension overhaul") deliberately replaced these emojis with CSS-driven icon spans for a consistent design system. The classes are fully defined in panel.css:3607–3705 with ::before pseudo-elements that render the actual glyphs.
Why this matters: emoji rendering varies wildly across OS/browser emoji fonts (the same
🔍looks very different on macOS Big Sur vs. Windows 11 vs. ChromeOS); the CSS-driven approach guarantees a single visual identity.
The reason the existing tests pass with emoji is a known stale-test bug (commit 12ed051 explicitly logs "29 pre-existing emoji-assertion failures"). Restoring emojis to pass tests is fixing the symptom, not the bug.
Fix: revert the emoji substitutions. Keep the CSS spans.
2. CSS class regression: learn-link → learn-more-link
Line 110 of the diff renames the <a> element's className from learn-link to learn-more-link. The CSS file (panel.css:561,631) only defines .learn-link and .learn-link:hover — .learn-more-link has no matching rule. The link will lose its color: var(--accent-blue), text-decoration, and hover styles, rendering as a plain unstyled <a>.
Fix: revert to className="learn-link" (or also rename the CSS rule, but the rename has no purpose so just revert).
3. .issue-actions wrapper <div> removed
The wrapper <div className="issue-actions"> (defined at panel.css:556–559 with display: flex; gap: 12px) is deleted, hoisting the <a> link out. The visual layout will shift if any second action button is ever added — and the .issue-actions CSS rule becomes orphaned.
Fix: restore the wrapper div.
🔍 Major — should fix before merge
4. import React, { useState } unnecessary
The project uses "jsx": "react-jsx" in tsconfig.json (automatic JSX runtime). React does not need to be in scope for JSX, and every other component in src/panel/ uses pure named imports (import { useState } from 'react').
Fix:
import { memo, useState } from 'react';
// ...
export const IssueCard = memo(function IssueCard({ issue }: IssueCardProps) { ... });This pattern preserves the named function so React DevTools shows IssueCard, not "Anonymous" (per react.dev memo examples and the precedent already in src/__tests__/debugging-scenarios.tsx:301).
5. JSX indentation broken in the <a> block
Lines 105–114 of the diff: the rewritten <a href={info.learnUrl} ...> element has inconsistent indentation — target and rel at column 12, 📚 Learn more at column 9, </a> at column 8, )} at column 8. Compare to lines 80–86 in the same file which use clean 2-space nesting.
Fix: reformat with consistent 2-space indentation (or run npx prettier --write src/panel/components/IssueCard.tsx if Prettier is installed).
6. Missing newline at EOF
src/panel/components/IssueCard.tsx ends without a trailing newline (diff line 121: \ No newline at end of file). Every other source file in the repo ends with a newline.
Fix: add a trailing newline.
7. package-lock.json hand-edit (also potentially impolite)
The package-lock.json diff (lines 5–13) adds a funding block to the root package metadata. This field belongs in package.json, not the auto-generated lockfile — the next npm install may revert or reformat it. And package.json:28 already has the funding field configured.
I appreciate the implicit support but please drop the lockfile change. If you want to add anything to package.json funding metadata, open a separate PR.
✅ Minor — nice-to-have, not blocking
- Whitespace-only changes throughout (trailing commas, blank-line removal on line 35, etc.) inflate the diff. If you keep them, mention them in the PR description.
- Empty PR body — please add a one-sentence description: e.g. "Wraps IssueCard in React.memo to skip re-renders when the
issueprop is reference-equal (parent state updates every 5s via POLL_DATA cycle)." This helps reviewers understand the perf measurement basis.
✅ What's good
React.memo(function IssueCard(...))named-function pattern is correct and DevTools-friendly.Issueprop is a stable reference (parents pass it directly fromstate.issues.map(...)) — default shallow equality is sufficient, no secondarePropsEqualarg needed (per the react.dev memo docs).- Closes a real perf issue (#25) and the underlying optimization is legitimate.
Suggested path forward
- Revert items 1, 2, 3 (emoji + CSS class + wrapper)
- Apply items 4, 5, 6 (named import, indentation, EOF newline)
- Remove the
package-lock.jsonchange - The final diff should be: one line for the
import, two lines forexport const IssueCard = memo(function IssueCard(...), one closing);, and a few blank-line normalizations. That's it.
Once that lands, this is a one-line-of-real-change PR that closes #25 cleanly. Happy to re-review immediately.
Reference: react.dev memo docs — "You should only rely on memo as a performance optimization." The wrap is justified here because POLL_DATA fires every 5s and IssueCards in long sessions can number 20+.
d716b28 to
df0c6eb
Compare
|
Hi @ELITE-DEV-22 — thanks for pushing the revision! Looking at 1. Emoji revert (still in the diff)The CSS-icon spans need to come back. In the current - <strong>🔍 Closure Timeline:</strong>
+ <strong><span className="action-badge action-badge--search" /> Closure Timeline:</strong>
- <strong>💡 Suggestion:</strong>
+ <strong><span className="action-badge action-badge--suggestion" /> Suggestion:</strong>
- 📚 Learn more
+ <span className="action-badge action-badge--learn" /> Learn moreThese CSS classes are defined in The Gemini code review flagged the same thing independently — both reviewers landed on this point. 2.
|
…w) (#48) Promotes the 'star the repo' rule from soft enforcement to a CI-blocking gate on PR merges. PRs from contributors who haven't starred the repo cannot merge until they do (single click + Re-run job). Mechanism: - New workflow .github/workflows/star-check.yml runs on every PR (opened, reopened, synchronize, ready_for_review, labeled, unlabeled). - Uses the public GitHub REST API 'GET /users/{login}/starred/{owner}/{repo}' via github-script. Returns 204 if starred, 404 if not. - No extra auth scope required beyond the default GITHUB_TOKEN. - Single job, 2 steps: the check + a summary write to the Actions UI. Auto-exemptions (no human action needed): - Maintainer PRs (@hoainho): hard-coded in the workflow's MAINTAINERS list. - Bot PRs: matches *[bot] suffix + an allowlist (dependabot, gemini-code-assist, google-cla, github-actions, renovate). - PRs labeled 'tracked-plan': for maintainer-driven milestone work (M-A, M-B, future Self-Roadmap milestones). - PRs labeled 'pre-star-rule': grandfathered PRs that were open before this policy landed (2026-06-01). Grandfathering (applied before this commit, separately via gh CLI): - 4 in-flight contributor PRs (#17, #36, #37, #38) labeled 'pre-star-rule' so the new check skips them. - 2 in-flight maintainer PRs (#39 M-A, #41 M-B) labeled 'tracked-plan' so the new check skips them. Failure UX: - When star check fails, the workflow writes a clear error message with: 'star the repo', 'click Re-run failed jobs', and a link to CONTRIBUTING.md. No re-push required to re-run after starring. Doc surface updated: - CONTRIBUTING.md: 'How to claim' section now flags the hard gate with a clear⚠️ notice; new 'Exemptions' subsection documents the 4 bypass categories. - PULL_REQUEST_TEMPLATE.md: 'Claim confirmation' section updated to note the CI enforcement. - CHANGELOG.md: new Unreleased entries under Added (workflow) + Changed (policy hardening) + Migration (grandfathering). Privacy + safety notes: - The API endpoint is public + read-only (no PII beyond GitHub's own public star list). - Workflow permissions are minimal: 'contents: read' + 'pull-requests: read'. No write access requested. - The check is idempotent and safe to re-run. Self-referential note: this PR itself will trigger the new workflow once merged. Author is @hoainho (in MAINTAINERS), so it auto-passes.
hoainho
left a comment
There was a problem hiding this comment.
@ELITE-DEV-22 Thanks for the badge-icon restoration in b116693 — that fixes the v2.0.0 design-system regression cleanly. The icon spans action-badge--search, action-badge--suggestion, and action-badge--learn are all back. ✅
However, two issues remain that block merge:
⛔ Scope creep — 5 invented ISSUE_INFO entries
b116693 (or earlier in this branch) adds 5 new entries to ISSUE_INFO:
DIRECT_STATE_MUTATIONDUPLICATE_KEYEXTRA_DEPUNNECESSARY_RERENDERDEV_MODE_IN_PROD
I searched the entire src/ tree:
$ grep -rE "DIRECT_STATE_MUTATION|DUPLICATE_KEY|EXTRA_DEP|UNNECESSARY_RERENDER|DEV_MODE_IN_PROD" src/
(no matches)
None of these issue type names exist anywhere in the codebase. No detector emits them. No type union includes them. The fallback info = ISSUE_INFO[issue.type] || { title: issue.type, why: '' } means they don't crash anything — but they're 25 lines of dead code that confuse future maintainers (looks like a feature spec, isn't).
This PR's title and stated goal is "memoize IssueCard to avoid unnecessary rerenders". Adding new issue-info copy is out of scope and should be a separate PR after the corresponding detectors land.
To fix: Revert the 5 added entries to keep the PR focused on the memoization fix.
# Edit IssueCard.tsx and delete the 5 new ISSUE_INFO entries (DIRECT_STATE_MUTATION, DUPLICATE_KEY, EXTRA_DEP, UNNECESSARY_RERENDER, DEV_MODE_IN_PROD)
git add src/panel/components/IssueCard.tsx
git commit -m "revert: remove ISSUE_INFO entries that lack corresponding detectors"
git push🟡 Minor — file-ending hygiene
IssueCard.tsx ends without a trailing newline (\ No newline at end of file in the diff). Most linters/CI flag this. Easy fix:
echo '' >> src/panel/components/IssueCard.tsx
git add src/panel/components/IssueCard.tsx && git commit --amend --no-edit && git push --force-with-lease✅ Looks good
React.memowrap is correct- Icon-badge spans correctly restored
- Whitespace + trailing-comma cleanups are fine
<div className="issue-actions">wrapper added (looks intentional based on commit message)
Once the 5 dead ISSUE_INFO entries are removed and the file ends with a newline, I'll approve and merge. You're 95% there — this is the last lap.
b116693 to
62f8441
Compare
No description provided.