Skip to content

perf: memoize IssueCard to avoid unnecessary rerenders#37

Open
ELITE-DEV-22 wants to merge 5 commits into
hoainho:mainfrom
ELITE-DEV-22:fix/memoize-issuecard
Open

perf: memoize IssueCard to avoid unnecessary rerenders#37
ELITE-DEV-22 wants to merge 5 commits into
hoainho:mainfrom
ELITE-DEV-22:fix/memoize-issuecard

Conversation

@ELITE-DEV-22
Copy link
Copy Markdown

No description provided.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/panel/components/IssueCard.tsx Outdated
Comment thread src/panel/components/IssueCard.tsx Outdated
Comment thread src/panel/components/IssueCard.tsx Outdated
@hoainho
Copy link
Copy Markdown
Owner

hoainho commented May 31, 2026

@ELITE-DEV-22 Please check comment from Gemini

Copy link
Copy Markdown
Owner

@hoainho hoainho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-linklearn-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 issue prop 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.
  • Issue prop is a stable reference (parents pass it directly from state.issues.map(...)) — default shallow equality is sufficient, no second arePropsEqual arg needed (per the react.dev memo docs).
  • Closes a real perf issue (#25) and the underlying optimization is legitimate.

Suggested path forward

  1. Revert items 1, 2, 3 (emoji + CSS class + wrapper)
  2. Apply items 4, 5, 6 (named import, indentation, EOF newline)
  3. Remove the package-lock.json change
  4. The final diff should be: one line for the import, two lines for export 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+.

@ELITE-DEV-22 ELITE-DEV-22 requested a review from hoainho May 31, 2026 17:43
@ELITE-DEV-22 ELITE-DEV-22 force-pushed the fix/memoize-issuecard branch from d716b28 to df0c6eb Compare May 31, 2026 17:54
@hoainho
Copy link
Copy Markdown
Owner

hoainho commented Jun 1, 2026

Hi @ELITE-DEV-22 — thanks for pushing the revision! Looking at df0c6eb, the whitespace cleanups look great, but 3 of the critical items from the earlier review are still present. Want to make sure you saw them — they may have gotten buried under all the comments. Re-summarizing here, very concretely:

1. Emoji revert (still in the diff)

The CSS-icon spans need to come back. In the current IssueCard.tsx:

- <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 more

These CSS classes are defined in panel.css:3607-3705 and render the icon via ::before pseudo-element. The v2.0.0 release deliberately moved off emojis because they render differently per-OS (different 🔍 glyph on macOS vs Windows vs ChromeOS).

The Gemini code review flagged the same thing independently — both reviewers landed on this point.

2. learn-link className change

Current diff: className="learn-more-link". But panel.css only defines .learn-link (lines 561, 631). The renamed class has no matching CSS rule, so the link will render unstyled (no blue color, no hover state).

Just revert: className="learn-link".

3. .issue-actions wrapper div removed

The original code had <div className="issue-actions"> wrapping the <a> link. That class is defined at panel.css:556 with display: flex; gap: 12px. The wrapper is needed for layout — please restore it.

What's NOT blocked

The React.memo wrap itself is good and stays. So is the named-function pattern. The PR is one revert-pass away from being mergeable.

Quickest path forward

If git is being weird, the simplest fix is:

# Get the original IssueCard.tsx from main
git checkout main -- src/panel/components/IssueCard.tsx

# Then apply ONLY the memo wrap:
# Line 1: change `import { useState }` to `import { memo, useState }`
# Line 82: change `export function IssueCard({...}` to
#         `export const IssueCard = memo(function IssueCard({...`
# Line ~End: add `);` after the closing brace of the function body

git add src/panel/components/IssueCard.tsx
git commit --amend  # or new commit
git push --force-with-lease

That should land you at a clean 2-3 line diff that closes #25 cleanly.

Happy to help if you hit a snag — just comment.

@hoainho hoainho added the pre-star-rule PR opened before the star-check rule landed (2026-06-01); exempt from Star Check CI label Jun 1, 2026
hoainho added a commit that referenced this pull request Jun 1, 2026
…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.
Copy link
Copy Markdown
Owner

@hoainho hoainho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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_MUTATION
  • DUPLICATE_KEY
  • EXTRA_DEP
  • UNNECESSARY_RERENDER
  • DEV_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.memo wrap 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.

@ELITE-DEV-22 ELITE-DEV-22 force-pushed the fix/memoize-issuecard branch from b116693 to 62f8441 Compare June 2, 2026 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pre-star-rule PR opened before the star-check rule landed (2026-06-01); exempt from Star Check CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants