This repository contains a set of focused React benchmark tasks that demonstrate real-world frontend bugs and their correct fixes, validated using automated tests.
Instead of building a full app, this project isolates common failure modes in React—performance issues, stale state, async correctness and shows how to fix them intentionally and testably.
Many React issues only appear in production:
- Unnecessary re-renders
- Stale closures
- Async race conditions
- Over-fetching due to user input
This project demonstrates how those bugs happen and how to fix them properly, with tests that prove the behavior change.
src/
tasks/
task-01-memo-optimization/
task-02-useeffect-stale-dependency/
task-03-async-race-condition/
task-04-debounce-and-cancellation/
Each task follows the same structure:
task-XX-*/
buggy/Component.tsx // Demonstrates the bug
fixed/Component.tsx // Correct implementation
tests/Component.test.tsx // Tests that prove the bug and the fix
Problem: An expensive child component re-renders unnecessarily whenever the parent state updates.
Buggy behavior:
- Child re-renders on every parent update
- Wastes CPU on expensive computations
Fix:
- Used
React.memoanduseCallback - Child only re-renders when its props actually change
What this demonstrates:
- React render behavior
- Memoization and performance optimization
- Preventing unnecessary re-renders
Problem:
Component does not update correctly when props change due to missing dependencies in useEffect.
Buggy behavior:
- UI shows stale data
- Effect does not re-run when expected
Fix:
- Correct dependency array usage
- Ensured state updates reflect latest props
What this demonstrates:
- Stale closures
- Correct
useEffectdependency management
Problem: Multiple async requests race, allowing older (slower) responses to overwrite newer state.
Buggy behavior:
- UI shows incorrect or outdated data
- Latest user action is ignored
Fix:
- Ensured only the latest async request updates state
- Prevented stale responses from winning
What this demonstrates:
- Async correctness
- Race condition handling in React
Problem: Search input fires requests on every keystroke and can display stale results.
Buggy behavior:
- Excessive network calls
- Older results overwrite newer input
Fix:
- Debounced user input
- Cancelled or ignored stale async requests
- Only latest user intent updates the UI
What this demonstrates:
- Debouncing
- Async cancellation
- Real-world search/input behavior
All tasks are validated using Vitest and React Testing Library.
Run all tests:
npm install
npx vitest runExpected result:
Test Files 4 passed
Tests 15 passed
This project focuses on how React fails in real applications and how to fix those failures deliberately, with tests that prove the improvement.
It is intentionally task-based and minimal, prioritizing clarity over boilerplate.
- 071-100: Complex scenarios and edge cases
This numbering allows you to insert new tasks between existing ones without renumbering.
- Focus: Each task should illustrate one clear problem
- Simplicity: Keep components small and easy to understand (avoid unrelated complexity)
- Comments: Use clear, concise comments in code to highlight the bug and fix
- Test Coverage: Tests should fail for the buggy version and pass for the fixed version
- Documentation: README should be clear enough for a developer unfamiliar with the topic to understand the issue after reading it
- Start the dev server:
npm run dev - Create or edit task files in
src/tasks/ - Write tests in
tests/subdirectory - Run tests frequently:
npm testornpm test:ui - Verify lint rules:
npm run lint - Build for production:
npm run build
- All components use TypeScript for type safety
- Components are functional components with hooks (no class components)
- Tests use Vitest and React Testing Library
- Code follows ESLint configuration defined in
eslint.config.js - Task numbering uses zero-padded two-digit format (01, 02, ..., 100)