Challenge: Refactoring a Slow Comment Viewer App

Debug and refactor a slow three-component comment viewer by stabilizing identities, fixing memo and selector behavior, and removing heavy render-phase work to make the UI responsive under React 19.

We'll cover the following...

Problem statement

You’ve been tasked with debugging a small React feature called CommentViewer, a compact UI that displays a list of user comments alongside a live analytics summary. The codebase is small, consisting of just three components: App, CommentList, and StatsPanel. Yet, users report noticeable lag when typing, filtering comments, or when new data arrives.

Upon inspecting the profiler, you notice several performance issues:

  • CommentList re-renders excessively, even though it is wrapped in React.memo.

  • StatsPanel performs heavy computations on every keystroke.

  • App generates unstable derived props, forcing unnecessary work throughout the component tree.

Your goal is to refactor this app so that it behaves like a well-architected React 19 component tree: stable identities, meaningful memo boundaries, correct selector usage, and smooth, responsive interactions.

Technical requirements: Implement the following tasks step by step:

Task 1: Fix App.js (identity stability)

  • Refactor App.js so that:

    • filter is stable across renders.

    • filteredComments is stable when the text hasn’t changed.

    • Transition usage is prepared for deferring heavy work (later in StatsPanel).

  • You can use useMemo as needed.

Task 2: Fix CommentList.js (Effective memo boundary)

  • After your refactor to App.js, CommentList should:

    • Re-render only when the visible comments truly change.

    • Ignore unrelated local UI refresh actions.

  • Make small additions if needed (e.g., memoizing derived child elements), but the main fix is stabilizing props in the parent.

Task 3: Fix StatsPanel.js (selector stability + heavy work)

  • Refactor StatsPanel so that:

    • The selector (snap) is stable and does not change shape on every render.

    • Heavy analytics computation is moved into useMemo or wrapped in a transition.

    • StatsPanel only re-renders when:

      • The comments meaningfully change, or

      • The multiplier changes in the store.

Task 4: Sanity checks and success criteria

  • Local UI refresh:

    • Click “Refresh UI Timestamp.”

    • After your fix:

      • App re-renders (expected)

      • CommentList render should not log

      • StatsPanel render should not log

    • If CommentList or StatsPanel still logs, props are still unstable.

  • Store refresh:

    • Trigger the store-refresh action (e.g., via store.setMultiplier or an existing store timestamp refresh control

    • After your fix:

      • StatsPanel render should not log unless the multiplier changed

      • CommentList render should never log.

    • If StatsPanel logs for unrelated store changes, your selector is not yet stable.

  • Typing behavior:

    • After the refactor, typing in the search bar should:

      • Feel noticeably smoother

      • Trigger far fewer CommentList render logs

      • Not trigger expensive StatsPanel render logs on every keypress

Faulty source code

Below is the broken codebase consisting of App.js, CommentList.js, and StatsPanel.js. All code runs without errors but contains realistic performance traps.