graph TD A[Learn React] --> B[Build Frontend] B --> C[Connect to Node/Express] C --> D[Full-Stack Developer! 🎉]
React Frontend Development
Building Interactive UIs with Modern React
Course lectures and practices for JavaScript full‑stack web development with AI‑assisted workflows.
React Frontend Development 🚀
2-Hour Focus: Components → State → Real Application
🎯 Today’s Mission
Hour 1: React Fundamentals (components, JSX, props, basic state)
Hour 2: Interactive Features (events, forms, data fetching, real app structure)
Using: fullstack-minimal-app
as our foundation
Goal: Build a complete React frontend ready for backend integration! 🎉
Philosophy: Learn by building, AI-assisted development, practical focus
🗺️ The Learning Path
Hour 1: REACT FOUNDATIONS Hour 2: INTERACTIVE FEATURES
┌─────────────────┐ ┌─────────────────┐
│ Why React? │ │ Event Handling │
│ JSX & Components│ → │ Forms & Input │
│ Props Passing │ │ API Integration │
│ Basic State │ │ Complete App! │
└─────────────────┘ └─────────────────┘
Foundation Ready Production Ready
Hour 1: React Foundations
0:00 - 1:00
💡 Why React? (The JavaScript Pain)
Remember vanilla JavaScript from Session 2?
// Manual DOM manipulation (tedious!)
const button = document.querySelector('#loadBtn');
const output = document.querySelector('#output');
.addEventListener('click', async () => {
button.innerHTML = 'Loading...';
outputconst response = await fetch('/api/data');
const data = await response.json();
// Manual HTML creation (error-prone!)
.innerHTML = '';
output.forEach(item => {
dataconst div = document.createElement('div');
.textContent = item.name;
div.appendChild(div);
output;
}); })
React Makes It Elegant:
function DataDisplay() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const loadData = async () => {
setLoading(true);
const response = await fetch('/api/data');
const result = await response.json();
setData(result);
setLoading(false);
;
}
return (
<div>
<button onClick={loadData}>Load Data</button>
{loading ? <p>Loading...</p> : null}
{data.map(item => <div key={item.id}>{item.name}</div>)}
</div>
;
) }
React = Declarative: “Here’s what the UI should look like” not “Here’s how to build it”
🚀 React in the Real World
What React Solves
- Component Reusability: Write once, use everywhere
- State Management: UI updates automatically
- Virtual DOM: Efficient updates
- Developer Experience: Great tools and ecosystem
- Community: Huge library ecosystem
Industry Usage
- Netflix: Entire streaming interface
- Instagram: Web platform
- Airbnb: Booking and host platforms
- Uber: Driver and rider apps
- Discord: Chat interface
Why You’re Learning React
Job Market: React is the #1 frontend framework
This Course: React + Node.js = Complete full-stack skills
Our Project Today: Product review aggregator frontend that will connect to Express backend next session!
🔧 Setup: fullstack-minimal-app
We’ll use the provided minimal app as our foundation
# Clone and setup
git clone https://github.com/your-repo/fullstack-minimal-app
cd fullstack-minimal-app
# Install dependencies
npm install
# Start development (opens both frontend and backend)
npm run dev
# Frontend: http://localhost:5173
# Backend: http://localhost:4000 (we'll use next session)
What you get:
fullstack-minimal-app/
├── frontend/ ← React app (Vite + React)
│ ├── src/
│ │ ├── App.jsx ← Main component
│ │ ├── main.jsx ← Entry point
│ │ └── components/ ← Our components go here
│ └── package.json
├── backend/ ← Node.js API (next session)
└── package.json ← Root scripts
VS Code Extensions: - GitHub Copilot - ES7+ React snippets - Tailwind CSS IntelliSense
AI Prompting Pattern:
Context: React functional component for [purpose]
Task: Create component that [specific functionality]
Constraints: Modern React hooks, Tailwind CSS
Output: Complete JSX component
🧩 Components: The Building Blocks
A React component is a function that returns JSX
Simple Component
function WelcomeMessage() {
return (
<div className="p-4 bg-blue-50 rounded">
<h1 className="text-2xl font-bold text-blue-800">
Welcome to React! 👋</h1>
<p className="text-blue-600">
You're building with components now!</p>
</div>
;
) }
Using Components
function App() {
return (
<div className="container mx-auto p-4">
<WelcomeMessage />
<WelcomeMessage />
<WelcomeMessage />
</div>
;
) }
Key Concepts: - Components are reusable (use multiple times) - Components are composable (nest inside each other) - Components use JSX (looks like HTML, but it’s JavaScript)
📝 JSX: JavaScript + HTML
JSX lets you write HTML-like syntax in JavaScript
JSX Rules
function UserProfile() {
const name = "Alice Johnson";
const age = 25;
const isOnline = true;
return (
<div className="user-card">
{/* JavaScript expressions in {} */}
<h2>Welcome, {name}!</h2>
<p>Age: {age}</p>
{/* Conditional rendering */}
{isOnline && (
<span className="status online">🟢 Online</span>
}
)
{/* Self-closing tags need / */}
<img src={`/avatar/${name}.jpg`} alt="Profile" />
<br />
{/* className not class */}
<button className="btn-primary">
Follow</button>
</div>
;
) }
HTML vs JSX Differences
HTML | JSX | Why? |
---|---|---|
class |
className |
class is reserved in JS |
for |
htmlFor |
for is reserved in JS |
onclick |
onClick |
camelCase convention |
style="color: red" |
style={{color: 'red'}} |
JS object syntax |
Expression Examples
// Variables
<h1>{title}</h1>
// Function calls
<p>{formatDate(date)}</p>
// Ternary operator
<span>{isVip ? '⭐ VIP' : 'Member'}</span>
// Array methods
.map(p => <div key={p.id}>{p.name}</div>)} {products
📦 Props: Passing Data
Props make components reusable by passing data into them
Creating a Component with Props
function ProductCard({ name, price, image, inStock }) {
return (
<div className="border rounded-lg p-4 shadow-md">
<img src={image} alt={name} className="w-full h-48 object-cover rounded" />
<h3 className="text-lg font-semibold mt-2">{name}</h3>
<p className="text-xl font-bold text-green-600">${price}</p>
{inStock ? (
<button className="w-full bg-blue-600 text-white py-2 rounded mt-2">
Add to Cart</button>
: (
) <button className="w-full bg-gray-400 text-white py-2 rounded mt-2" disabled>
Out of Stock</button>
}
)</div>
;
) }
Using the Component with Different Data
function ProductGrid() {
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<ProductCard
name="Wireless Headphones"
price={99.99}
image="/images/headphones.jpg"
inStock={true}
/>
<ProductCard
name="Bluetooth Speaker"
price={79.99}
image="/images/speaker.jpg"
inStock={false}
/>
<ProductCard
name="Smart Watch"
price={299.99}
image="/images/watch.jpg"
inStock={true}
/>
</div>
;
) }
Props Pattern: Define once, use with different data!
🤖 AI Practice: Product Card Component
Prompt for AI:
Context: React component for displaying product reviews
Task: Create a ReviewCard component with these props:
- author (string): reviewer name
- rating (number): 1-5 stars
- title (string): review title
- content (string): review text
- date (string): review date
- source (string): "Amazon" | "BestBuy" | "Walmart"
Requirements:
- Display star rating as ★ symbols
- Show source with colored badge (Amazon=orange, BestBuy=blue, Walmart=green)
- Responsive design with Tailwind CSS
- Modern functional component syntax
Output: Complete ReviewCard.jsx component
After AI generates: 1. Create the component in frontend/src/components/ReviewCard.jsx
2. Test it in your App component 3. Try it with different prop values
🔄 State: Making Components Interactive
State = data that can change and triggers UI updates
useState Hook
import { useState } from 'react';
function Counter() {
// [value, setter] = useState(initialValue)
const [count, setCount] = useState(0);
return (
<div className="text-center p-4">
<h2 className="text-2xl mb-4">Count: {count}</h2>
<div className="space-x-2">
<button
onClick={() => setCount(count - 1)}
className="px-4 py-2 bg-red-500 text-white rounded"
>
-1</button>
<button
onClick={() => setCount(count + 1)}
className="px-4 py-2 bg-green-500 text-white rounded"
>
+1</button>
<button
onClick={() => setCount(0)}
className="px-4 py-2 bg-gray-500 text-white rounded"
>
Reset</button>
</div>
</div>
;
) }
Key Points: - Always use the setter function (setCount
, not count = 5
) - State updates trigger re-render - Each component has its own state
🎯 Toggle State Example
function ProductDetails() {
const [showDetails, setShowDetails] = useState(false);
const [quantity, setQuantity] = useState(1);
return (
<div className="border p-4 rounded">
<h3 className="text-xl font-bold">Wireless Headphones</h3>
<p className="text-green-600 font-semibold">$99.99</p>
<button
onClick={() => setShowDetails(!showDetails)}
className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
>
{showDetails ? 'Hide Details' : 'Show Details'}
</button>
{showDetails && (
<div className="mt-4 p-3 bg-gray-50 rounded">
<h4 className="font-semibold">Product Details:</h4>
<ul className="list-disc list-inside mt-2">
<li>Wireless Bluetooth 5.0</li>
<li>30-hour battery life</li>
<li>Active noise cancellation</li>
</ul>
</div>
}
)
<div className="mt-4">
<label className="block text-sm font-medium">Quantity:</label>
<input
type="number"
value={quantity}
onChange={(e) => setQuantity(parseInt(e.target.value))}
min="1"
className="mt-1 border rounded px-3 py-2"
/>
</div>
</div>
;
) }
🏃 Quick Practice: State Components
Create a simple component with state:
Option 1: Like/Unlike button with counter Option 2: Show/hide product reviews section
Option 3: Color theme switcher
Use AI to generate, then test in your app!
Before break, you should have: - ✅ Understanding of components and JSX - ✅ Created components with props - ✅ Used useState for interactive features - ✅ Built at least one working component
Hour 2 Preview: Forms, API calls, complete application!
Hour 2: Interactive Features
1:00 - 2:00
🎮 Event Handling
React handles events with camelCase: onClick
, onChange
, onSubmit
Common Event Patterns
function InteractiveDemo() {
const [message, setMessage] = useState('Click something!');
const [inputValue, setInputValue] = useState('');
const handleButtonClick = () => {
setMessage('Button was clicked! 🎉');
;
}
const handleInputChange = (event) => {
setInputValue(event.target.value);
setMessage(`You typed: ${event.target.value}`);
;
}
const handleMouseEnter = () => {
setMessage('Mouse entered the area! 🐭');
;
}
return (
<div className="p-4 space-y-4">
<p className="text-lg font-semibold">{message}</p>
<button
onClick={handleButtonClick}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Click Me</button>
<input
type="text"
value={inputValue}
onChange={handleInputChange}
placeholder="Type something..."
className="border rounded px-3 py-2"
/>
<div
onMouseEnter={handleMouseEnter}
className="p-4 bg-yellow-100 rounded cursor-pointer"
>
Hover over me!</div>
</div>
;
) }
📝 Forms & Controlled Inputs
React controls form inputs = “Controlled Components”
Product Search Form
function ProductSearch() {
const [searchTerm, setSearchTerm] = useState('');
const [category, setCategory] = useState('all');
const [priceRange, setPriceRange] = useState('any');
const [results, setResults] = useState([]);
const handleSubmit = (event) => {
event.preventDefault(); // Important: prevent page reload!
console.log('Searching for:', {
,
searchTerm,
category
priceRange;
})
// Simulate search results
setResults([
`Results for "${searchTerm}" in ${category}`,
`Price range: ${priceRange}`,
`Found 3 products matching your criteria`
;
]);
}
return (
<div className="max-w-md mx-auto p-4">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1">
Search Products:</label>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Enter product name..."
className="w-full border rounded px-3 py-2"
required
/>
</div>
<div>
<label className="block text-sm font-medium mb-1">
Category:</label>
<select
value={category}
onChange={(e) => setCategory(e.target.value)}
className="w-full border rounded px-3 py-2"
>
<option value="all">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
<option value="books">Books</option>
</select>
</div>
<div>
<label className="block text-sm font-medium mb-1">
Price Range:</label>
<select
value={priceRange}
onChange={(e) => setPriceRange(e.target.value)}
className="w-full border rounded px-3 py-2"
>
<option value="any">Any Price</option>
<option value="under-50">Under $50</option>
<option value="50-100">$50 - $100</option>
<option value="over-100">Over $100</option>
</select>
</div>
<button
type="submit"
className="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700"
>
Search Products</button>
</form>
{results.length > 0 && (
<div className="mt-4 p-3 bg-green-50 rounded">
<h3 className="font-semibold">Search Results:</h3>
<ul className="mt-2">
{results.map((result, index) => (
<li key={index} className="text-sm">{result}</li>
}
))</ul>
</div>
}
)</div>
;
) }
Pattern: value={state}
+ onChange={setState}
= Controlled Input
🌐 API Integration with useEffect
useEffect runs code after render (perfect for API calls)
Fetching Data on Component Mount
import { useState, useEffect } from 'react';
function ProductList() {
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Runs once when component mounts
useEffect(() => {
const fetchProducts = async () => {
try {
setLoading(true);
const response = await fetch('http://localhost:4000/api/products');
if (!response.ok) {
throw new Error('Failed to fetch products');
}
const data = await response.json();
setProducts(data);
catch (err) {
} setError(err.message);
finally {
} setLoading(false);
};
}
fetchProducts();
, []); // Empty dependency array = run once on mount
}
if (loading) {
return (
<div className="text-center p-8">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto"></div>
<p className="mt-2">Loading products...</p>
</div>
;
)
}
if (error) {
return (
<div className="text-center p-8 text-red-600">
<p>Error: {error}</p>
<button
onClick={() => window.location.reload()}
className="mt-2 px-4 py-2 bg-red-600 text-white rounded"
>
Try Again</button>
</div>
;
)
}
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 p-4">
{products.map(product => (
<div key={product.id} className="border rounded-lg p-4 shadow">
<h3 className="text-lg font-semibold">{product.name}</h3>
<p className="text-green-600 font-bold">${product.price}</p>
<p className="text-gray-600 text-sm mt-1">{product.description}</p>
<button className="mt-2 w-full bg-blue-600 text-white py-2 rounded">
View Details</button>
</div>
}
))</div>
;
) }
🤖 AI Practice: Review Fetcher Component
Prompt for AI:
Context: React component for fetching product reviews
Task: Create ReviewFetcher component that:
1. Has a "Fetch Reviews" button
2. When clicked, calls POST /api/products/:id/fetch
3. Shows loading state while fetching
4. Displays success message with review count
5. Handles errors gracefully
6. Uses useState and async/await
Props:
- productId (string): ID of product to fetch reviews for
Requirements:
- Loading spinner during fetch
- Success/error messages
- Tailwind CSS styling
- Modern React hooks
Output: Complete React component
Test with: Mock API call (will work with real backend next session)
🏗️ Building the Complete App Structure
Let’s structure our review aggregator application
App Component Structure
// App.jsx
import { useState } from 'react';
import ProductHeader from './components/ProductHeader';
import ReviewFetcher from './components/ReviewFetcher';
import ReviewStats from './components/ReviewStats';
import ReviewList from './components/ReviewList';
function App() {
const [reviews, setReviews] = useState([]);
const [stats, setStats] = useState(null);
const productId = "sample-product-1"; // In real app, this comes from URL
const handleReviewsFetched = (newReviews, newStats) => {
setReviews(newReviews);
setStats(newStats);
;
}
return (
<div className="min-h-screen bg-gray-50">
<div className="container mx-auto px-4 py-8">
<ProductHeader productId={productId} />
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 mt-8">
{/* Left Column: Actions and Stats */}
<div className="lg:col-span-1 space-y-6">
<ReviewFetcher
productId={productId}
onReviewsFetched={handleReviewsFetched}
/>
{stats && (
<ReviewStats stats={stats} />
}
)</div>
{/* Right Column: Review List */}
<div className="lg:col-span-2">
<ReviewList reviews={reviews} />
</div>
</div>
</div>
</div>
;
)
}
export default App;
Component Organization
frontend/src/
├── components/
│ ├── ProductHeader.jsx ← Product info display
│ ├── ReviewFetcher.jsx ← Fetch reviews button
│ ├── ReviewStats.jsx ← Statistics visualization
│ ├── ReviewList.jsx ← List of reviews
│ └── ReviewCard.jsx ← Individual review display
├── hooks/
│ └── useReviews.js ← Custom hook for review logic
├── services/
│ └── api.js ← API calls
└── App.jsx ← Main app component
🎯 Complete Example: Review Stats Component
function ReviewStats({ stats }) {
if (!stats) {
return (
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">Review Statistics</h3>
<p className="text-gray-500">No statistics available yet.</p>
</div>
;
)
}
const { overallAverage, totalReviews, sourceBreakdown, ratingHistogram } = stats;
return (
<div className="bg-white rounded-lg shadow p-6">
<h3 className="text-lg font-semibold mb-4">Review Statistics</h3>
{/* Overall Stats */}
<div className="text-center mb-6">
<div className="text-3xl font-bold text-blue-600">
{overallAverage.toFixed(1)}★
</div>
<p className="text-gray-600">{totalReviews} total reviews</p>
</div>
{/* Source Breakdown */}
<div className="mb-6">
<h4 className="font-medium mb-2">By Source:</h4>
{sourceBreakdown.map(source => (
<div key={source.name} className="flex justify-between items-center py-1">
<span className="text-sm">{source.name}</span>
<span className="text-sm font-semibold">
{source.average.toFixed(1)}★ ({source.count})
</span>
</div>
}
))</div>
{/* Rating Histogram */}
<div>
<h4 className="font-medium mb-2">Rating Distribution:</h4>
{[5, 4, 3, 2, 1].map(rating => {
const count = ratingHistogram[rating] || 0;
const percentage = totalReviews > 0 ? (count / totalReviews) * 100 : 0;
return (
<div key={rating} className="flex items-center mb-1">
<span className="text-sm w-8">{rating}★</span>
<div className="flex-1 bg-gray-200 rounded-full h-2 mx-2">
<div
className="bg-blue-600 h-2 rounded-full"
style={{ width: `${percentage}%` }}
></div>
</div>
<span className="text-sm w-8 text-right">{count}</span>
</div>
;
)}
})</div>
</div>
;
) }
🏁 Session Wrap-up
What You’ve Learned Today:
React Fundamentals ✅
- Components and JSX syntax
- Props for data passing
- State with useState hook
- Event handling patterns
- Controlled form inputs
- useEffect for side effects
Practical Skills ✅
- Building reusable components
- Managing application state
- Handling user interactions
- API integration patterns
- Error handling and loading states
Application Structure ✅
- Component organization
- Data flow patterns
- State management strategies
- Real-world app architecture
AI-Assisted Development ✅
- Effective prompting techniques
- Code verification skills
- Iterative development process
- Debugging generated code
Next Session Preview: Build the Node.js/Express backend that will power this React frontend!
📚 Practice Session Preview
Immediately after this lecture: 1-hour hands-on practice building your review aggregator frontend components using the patterns learned today.
What you’ll build: - Complete React frontend for the Multi-Source Product Review Aggregator - Components for displaying and fetching reviews - Integration with mock API (preparing for real backend) - Responsive design with Tailwind CSS
This prepares you for: Next session’s backend development where you’ll build the Express API that serves this frontend!
🎉 You’re Now a React Developer!
Key Achievements: - ⚡ Built interactive UIs with components - 🔄 Managed state and handled events - 🌐 Integrated with APIs using useEffect - 🎨 Created responsive layouts - 🤖 Used AI effectively for development - 🏗️ Structured a real application
Next Steps: Practice building the review aggregator, then learn Node.js to create the backend! 🚀