Skip to main content

Async Posts List

Difficulty: Advanced

Problem Statement​

Fetch posts on mount, show loading, render list, support filter action, handle failures gracefully.

Live Demo​

Try the running app below β€” this is the target behavior for the challenge.

Async Posts List β€” Live Demo

Loading…

What Interviewers Expect​

  • Loading/error/success branches.
  • Effect cleanup with mounted flag or abort.
  • Test-friendly selectors (data-testid).

Step-by-Step Implementation​

Step 1 β€” Async effect​

Fetch on mount and guard setState after unmount.

useEffect(() => { let mounted = true; ... return () => { mounted = false; }; }, []);

Step 2 β€” Loading UI​

Render loading state until data arrives.

if (!posts) return <p>Loading…</p>;

Step 3 β€” Filter action​

Support user action to slice visible posts.

const visible = filtered ? posts.slice(0, 2) : posts;

Complete Implementation​

Copy-paste ready single-file solution. Use this as your reference after attempting the challenge yourself, or paste it into CodeSandbox / StackBlitz to run locally.

App.jsx
import React, {useEffect, useState} from 'react';

const MOCK_POSTS = [
{id: 1, title: 'React state basics'},
{id: 2, title: 'Machine coding tips'},
{id: 3, title: 'Testing async UI'},
{id: 4, title: 'Transfer list patterns'},
];

export default function App() {
const [posts, setPosts] = useState(null);
const [error, setError] = useState('');
const [filtered, setFiltered] = useState(false);

useEffect(() => {
let mounted = true;
const timer = setTimeout(() => {
if (mounted) setPosts(MOCK_POSTS);
}, 700);

return () => {
mounted = false;
clearTimeout(timer);
};
}, []);

if (error) return <p>{error}</p>;
if (!posts) return <p>Loading…</p>;

const visible = filtered ? posts.slice(0, 2) : posts;

return (
<div>
<button type="button" onClick={() => setFiltered(true)}>
Filter first 2
</button>
<ul data-testid="list">
{visible.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}

Final Checklist​

  • UI works for primary flow
  • Edge cases handled (empty/disabled/loading where relevant)
  • State updates are immutable
  • Components are readable and reasonably split
  • You can explain trade-offs out loud in an interview

Next Challenge​

Continue to the next item in the sidebar when you're comfortable implementing this from scratch in 30–45 minutes.