feat: add Feed page and Item component (migration step 3)#246
feat: add Feed page and Item component (migration step 3)#246devin-ai-integration[bot] wants to merge 2 commits into
Conversation
…ep 3) - Create src/pages/Feed.tsx: ported from feed.component.ts/.html - Uses useParams for feedType/page, useReducer for state management - Renders Item list with pagination (Prev/More) via react-router Link - Shows Loader while loading, ErrorMessage on error - Jobs feed header support - Create src/components/Item.tsx: ported from item.component.ts/.html - Accepts Story prop, uses useSettings for font size, spacing, openLinkInNewTab - Uses formatCommentCount utility for comment display - Links to /item/:id for comments and /user/:user for profiles - Responsive layout with subtext-palm (mobile) and subtext-laptop (desktop) - Add corresponding SCSS files ported from Angular originals Co-Authored-By: Eashan Sinha <eashan.sinha@cognition.ai>
Original prompt from Eashan
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| useEffect(() => { | ||
| dispatch({ type: 'loading' }); | ||
|
|
||
| fetchFeed(feedType, pageNum) | ||
| .then((data) => { | ||
| dispatch({ type: 'loaded', items: data }); | ||
| window.scrollTo(0, 0); | ||
| }) | ||
| .catch(() => { | ||
| dispatch({ | ||
| type: 'error', | ||
| message: `Could not load ${feedType} stories.`, | ||
| }); | ||
| }); | ||
| }, [feedType, pageNum]); |
There was a problem hiding this comment.
🟡 Race condition: stale fetch response overwrites current data when feedType or page changes rapidly
The useEffect in Feed.tsx:43-57 fires a new fetchFeed call whenever feedType or pageNum changes, but never cancels the previous in-flight request. If a user navigates quickly (e.g., page 1 → page 2 → page 3), multiple fetches run concurrently. If an earlier fetch resolves after a later one, its stale data overwrites the current results via dispatch({ type: 'loaded', items: data }), causing the UI to display data from a page/feed the user has already navigated away from. The fix is to use an AbortController or a stale-closure guard (boolean flag set in the cleanup function) to ignore responses from superseded requests.
Example scenario
- User on page 1 clicks "More" → effect fires fetch for page 2
- User immediately clicks "More" again → effect fires fetch for page 3, dispatches 'loading'
- Page 3 fetch completes first → UI shows page 3 data
- Page 2 fetch completes later → UI now incorrectly shows page 2 data while URL says page 3
| useEffect(() => { | |
| dispatch({ type: 'loading' }); | |
| fetchFeed(feedType, pageNum) | |
| .then((data) => { | |
| dispatch({ type: 'loaded', items: data }); | |
| window.scrollTo(0, 0); | |
| }) | |
| .catch(() => { | |
| dispatch({ | |
| type: 'error', | |
| message: `Could not load ${feedType} stories.`, | |
| }); | |
| }); | |
| }, [feedType, pageNum]); | |
| useEffect(() => { | |
| let cancelled = false; | |
| dispatch({ type: 'loading' }); | |
| fetchFeed(feedType, pageNum) | |
| .then((data) => { | |
| if (!cancelled) { | |
| dispatch({ type: 'loaded', items: data }); | |
| window.scrollTo(0, 0); | |
| } | |
| }) | |
| .catch(() => { | |
| if (!cancelled) { | |
| dispatch({ | |
| type: 'error', | |
| message: `Could not load ${feedType} stories.`, | |
| }); | |
| } | |
| }); | |
| return () => { cancelled = true; }; | |
| }, [feedType, pageNum]); |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Good catch — added a stale-closure guard (cancelled flag) in the cleanup function to ignore responses from superseded requests. Fixed in 3073209.
Co-Authored-By: Eashan Sinha <eashan.sinha@cognition.ai>
Summary
Implements Step 3 of the Angular-to-React migration: the Feed page and Item component.
src/pages/Feed.tsxPorted from
src/app/feeds/feed/feed.component.ts+feed.component.html:useParams()from react-router-dom to extractpageparam; receivesfeedTypeas a propuseReducerfor state management (loading → loaded/error), avoiding synchronoussetStatein effectsfetchFeed()fromsrc/api/hackerNewsApi.tswheneverfeedTypeorpageNumchanges<Item>components inside an ordered list with correctstartnumbering<Link><Loader />while loading,<ErrorMessage />on fetch failuresrc/components/Item.tsxPorted from
src/app/feeds/item/item.component.ts+item.component.html:item: Storyandindexas propsuseSettings()to applytitleFontSize,listSpacing, andopenLinkInNewTabformatCommentCount()fromsrc/utils/formatCommentCount.ts<a href>, internal stories link to/item/:idvia<Link>/user/:usersubtext-palm(mobile) andsubtext-laptop(desktop)Styles
SCSS files ported from the Angular originals, updated to use
@useinstead of@importfor Sass module system compatibility.Review & Testing Checklist for Human
news,newest,show,ask,jobs) once routing is wired upsubtext-palmvisible on mobile,subtext-laptopon desktopNotes
tsc -b && vite build) and lint (eslint .) both pass cleanly.Link to Devin session: https://app.devin.ai/sessions/d7454e226af841039b9d379447226356
Requested by: @eashansinha
Devin Review