React - Building Modern Interactive User Interfaces
React has transformed the way we build web applications. With its component-based architecture and virtual DOM, it makes creating interactive UIs simple and efficient. Let's build a complete application and explore React's powerful features!
๐ Why React?โ
React dominates the frontend landscape for good reasons:
- ๐ฏ Component-Based: Build encapsulated components that manage their own state
- โก Virtual DOM: Lightning-fast updates and rendering
- ๐ Declarative: Describe how your UI should look, React handles the rest
- ๐ ๏ธ Developer Tools: Excellent debugging and development experience
- ๐ Massive Ecosystem: Thousands of libraries and tools
- ๐ผ Industry Standard: Used by Facebook, Netflix, Airbnb, and more
React vs Other Frameworksโ
| Feature | React | Vue | Angular |
|---|---|---|---|
| Learning Curve | Moderate | Easy | Steep |
| Performance | Excellent | Excellent | Good |
| Bundle Size | Small | Smallest | Large |
| Community | Huge | Growing | Large |
| Best For | All sizes | Small-Medium | Enterprise |
๐ Getting Startedโ
Prerequisitesโ
# Check Node.js version (18+)
node --version
# Check npm version
npm --version
Create React Appโ
# Using Create React App (CRA)
npx create-react-app my-task-app
cd my-task-app
# Or use Vite (faster, modern)
npm create vite@latest my-task-app -- --template react
cd my-task-app
npm install
Project Structureโ
my-task-app/
โโโ public/
โ โโโ index.html
โโโ src/
โ โโโ components/
โ โ โโโ TaskList.jsx
โ โ โโโ TaskItem.jsx
โ โ โโโ TaskForm.jsx
โ โโโ hooks/
โ โ โโโ useTasks.js
โ โโโ context/
โ โ โโโ TaskContext.jsx
โ โโโ utils/
โ โ โโโ helpers.js
โ โโโ App.jsx
โ โโโ App.css
โ โโโ index.js
โโโ package.json
โโโ README.md
๐ฏ Building a Task Management Appโ
Let's build a complete task management application with modern React patterns.
Step 1: Basic Component Structureโ
Create src/App.jsx:
import React, { useState, useEffect } from 'react';
import './App.css';
import TaskForm from './components/TaskForm';
import TaskList from './components/TaskList';
import TaskStats from './components/TaskStats';
function App() {
const [tasks, setTasks] = useState([]);
const [filter, setFilter] = useState('all');
// Load tasks from localStorage on mount
useEffect(() => {
const savedTasks = localStorage.getItem('tasks');
if (savedTasks) {
setTasks(JSON.parse(savedTasks));
}
}, []);
// Save tasks to localStorage whenever they change
useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(tasks));
}, [tasks]);
// Add new task
const addTask = (taskText) => {
const newTask = {
id: Date.now(),
text: taskText,
completed: false,
createdAt: new Date().toISOString(),
};
setTasks([...tasks, newTask]);
};
// Toggle task completion
const toggleTask = (id) => {
setTasks(tasks.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
));
};
// Delete task
const deleteTask = (id) => {
setTasks(tasks.filter(task => task.id !== id));
};
// Edit task
const editTask = (id, newText) => {
setTasks(tasks.map(task =>
task.id === id ? { ...task, text: newText } : task
));
};
// Clear completed tasks
const clearCompleted = () => {
setTasks(tasks.filter(task => !task.completed));
};
// Filter tasks
const filteredTasks = tasks.filter(task => {
if (filter === 'active') return !task.completed;
if (filter === 'completed') return task.completed;
return true;
});
return (
<div className="app">
<header className="app-header">
<h1>โ๏ธ React Task Manager</h1>
<p>Stay organized and productive</p>
</header>
<main className="app-main">
<TaskForm onAddTask={addTask} />
<TaskStats
total={tasks.length}
completed={tasks.filter(t => t.completed).length}
active={tasks.filter(t => !t.completed).length}
/>
<div className="filter-buttons">
<button
className={filter === 'all' ? 'active' : ''}
onClick={() => setFilter('all')}
>
All
</button>
<button
className={filter === 'active' ? 'active' : ''}
onClick={() => setFilter('active')}
>
Active
</button>
<button
className={filter === 'completed' ? 'active' : ''}
onClick={() => setFilter('completed')}
>
Completed
</button>
</div>
<TaskList
tasks={filteredTasks}
onToggleTask={toggleTask}
onDeleteTask={deleteTask}
onEditTask={editTask}
/>
{tasks.some(t => t.completed) && (
<button className="clear-completed" onClick={clearCompleted}>
Clear Completed
</button>
)}
</main>
</div>
);
}
export default App;
Step 2: Task Form Componentโ
Create src/components/TaskForm.jsx:
import React, { useState } from 'react';
import './TaskForm.css';
function TaskForm({ onAddTask }) {
const [taskText, setTaskText] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
// Validation
if (!taskText.trim()) {
setError('Task cannot be empty');
return;
}
if (taskText.length > 100) {
setError('Task is too long (max 100 characters)');
return;
}
// Add task
onAddTask(taskText.trim());
// Reset form
setTaskText('');
setError('');
};
return (
<form className="task-form" onSubmit={handleSubmit}>
<div className="input-group">
<input
type="text"
value={taskText}
onChange={(e) => {
setTaskText(e.target.value);
setError('');
}}
placeholder="What needs to be done?"
className={error ? 'error' : ''}
maxLength={100}
/>
<button type="submit">
Add Task
</button>
</div>
{error && <p className="error-message">{error}</p>}
<p className="char-count">
{taskText.length}/100 characters
</p>
</form>
);
}
export default TaskForm;
Step 3: Task List Componentโ
Create src/components/TaskList.jsx:
import React from 'react';
import TaskItem from './TaskItem';
import './TaskList.css';
function TaskList({ tasks, onToggleTask, onDeleteTask, onEditTask }) {
if (tasks.length === 0) {
return (
<div className="empty-state">
<div className="empty-icon">๐</div>
<h3>No tasks yet</h3>
<p>Add a task to get started!</p>
</div>
);
}
return (
<ul className="task-list">
{tasks.map(task => (
<TaskItem
key={task.id}
task={task}
onToggle={onToggleTask}
onDelete={onDeleteTask}
onEdit={onEditTask}
/>
))}
</ul>
);
}
export default TaskList;
Step 4: Task Item Componentโ
Create src/components/TaskItem.jsx:
import React, { useState } from 'react';
import './TaskItem.css';
function TaskItem({ task, onToggle, onDelete, onEdit }) {
const [isEditing, setIsEditing] = useState(false);
const [editText, setEditText] = useState(task.text);
const handleEdit = () => {
if (editText.trim() && editText !== task.text) {
onEdit(task.id, editText.trim());
} else {
setEditText(task.text);
}
setIsEditing(false);
};
const handleKeyDown = (e) => {
if (e.key === 'Enter') {
handleEdit();
} else if (e.key === 'Escape') {
setEditText(task.text);
setIsEditing(false);
}
};
return (
<li className={`task-item ${task.completed ? 'completed' : ''}`}>
<div className="task-content">
<input
type="checkbox"
checked={task.completed}
onChange={() => onToggle(task.id)}
className="task-checkbox"
/>
{isEditing ? (
<input
type="text"
value={editText}
onChange={(e) => setEditText(e.target.value)}
onBlur={handleEdit}
onKeyDown={handleKeyDown}
className="task-edit-input"
autoFocus
/>
) : (
<span
className="task-text"
onDoubleClick={() => !task.completed && setIsEditing(true)}
>
{task.text}
</span>
)}
</div>
<div className="task-actions">
{!task.completed && !isEditing && (
<button
onClick={() => setIsEditing(true)}
className="btn-edit"
title="Edit task"
>
โ๏ธ
</button>
)}
<button
onClick={() => onDelete(task.id)}
className="btn-delete"
title="Delete task"
>
๐๏ธ
</button>
</div>
<div className="task-meta">
<small>{new Date(task.createdAt).toLocaleDateString()}</small>
</div>
</li>
);
}
export default TaskItem;
Step 5: Task Stats Componentโ
Create src/components/TaskStats.jsx:
import React from 'react';
import './TaskStats.css';
function TaskStats({ total, completed, active }) {
const completionRate = total > 0 ? Math.round((completed / total) * 100) : 0;
return (
<div className="task-stats">
<div className="stat-card">
<div className="stat-icon">๐</div>
<div className="stat-content">
<p className="stat-value">{total}</p>
<p className="stat-label">Total Tasks</p>
</div>
</div>
<div className="stat-card">
<div className="stat-icon">โ
</div>
<div className="stat-content">
<p className="stat-value">{completed}</p>
<p className="stat-label">Completed</p>
</div>
</div>
<div className="stat-card">
<div className="stat-icon">โณ</div>
<div className="stat-content">
<p className="stat-value">{active}</p>
<p className="stat-label">Active</p>
</div>
</div>
<div className="stat-card">
<div className="stat-icon">๐</div>
<div className="stat-content">
<p className="stat-value">{completionRate}%</p>
<p className="stat-label">Completion</p>
</div>
</div>
</div>
);
}
export default TaskStats;
๐จ Styling with CSSโ
Create src/App.css:
.app {
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 2rem;
}
.app-header {
text-align: center;
color: white;
margin-bottom: 2rem;
}
.app-header h1 {
font-size: 3rem;
margin-bottom: 0.5rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.2);
}
.app-main {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 20px;
padding: 2rem;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
}
.filter-buttons {
display: flex;
gap: 1rem;
margin: 2rem 0;
}
.filter-buttons button {
flex: 1;
padding: 0.75rem;
border: 2px solid #e0e0e0;
background: white;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s;
}
.filter-buttons button.active {
background: #667eea;
color: white;
border-color: #667eea;
}
.clear-completed {
width: 100%;
margin-top: 2rem;
padding: 1rem;
background: #ff6b6b;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition: background 0.3s;
}
.clear-completed:hover {
background: #ff5252;
}
๐ฃ Custom Hooksโ
Create src/hooks/useTasks.js:
import { useState, useEffect } from 'react';
export function useTasks() {
const [tasks, setTasks] = useState([]);
const [loading, setLoading] = useState(true);
// Load tasks from localStorage
useEffect(() => {
try {
const savedTasks = localStorage.getItem('tasks');
if (savedTasks) {
setTasks(JSON.parse(savedTasks));
}
} catch (error) {
console.error('Error loading tasks:', error);
} finally {
setLoading(false);
}
}, []);
// Save tasks to localStorage
useEffect(() => {
if (!loading) {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
}, [tasks, loading]);
const addTask = (taskText) => {
const newTask = {
id: Date.now(),
text: taskText,
completed: false,
createdAt: new Date().toISOString(),
};
setTasks(prev => [...prev, newTask]);
};
const toggleTask = (id) => {
setTasks(prev =>
prev.map(task =>
task.id === id ? { ...task, completed: !task.completed } : task
)
);
};
const deleteTask = (id) => {
setTasks(prev => prev.filter(task => task.id !== id));
};
const editTask = (id, newText) => {
setTasks(prev =>
prev.map(task =>
task.id === id ? { ...task, text: newText } : task
)
);
};
const clearCompleted = () => {
setTasks(prev => prev.filter(task => !task.completed));
};
return {
tasks,
loading,
addTask,
toggleTask,
deleteTask,
editTask,
clearCompleted,
};
}
๐ Context API for State Managementโ
Create src/context/TaskContext.jsx:
import React, { createContext, useContext, useState, useEffect } from 'react';
const TaskContext = createContext();
export function TaskProvider({ children }) {
const [tasks, setTasks] = useState([]);
const [filter, setFilter] = useState('all');
// Load and save tasks
useEffect(() => {
const saved = localStorage.getItem('tasks');
if (saved) setTasks(JSON.parse(saved));
}, []);
useEffect(() => {
localStorage.setItem('tasks', JSON.stringify(tasks));
}, [tasks]);
const addTask = (text) => {
setTasks([...tasks, {
id: Date.now(),
text,
completed: false,
createdAt: new Date().toISOString(),
}]);
};
const toggleTask = (id) => {
setTasks(tasks.map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
));
};
const deleteTask = (id) => {
setTasks(tasks.filter(t => t.id !== id));
};
const value = {
tasks,
filter,
setFilter,
addTask,
toggleTask,
deleteTask,
};
return (
<TaskContext.Provider value={value}>
{children}
</TaskContext.Provider>
);
}
export function useTasks() {
const context = useContext(TaskContext);
if (!context) {
throw new Error('useTasks must be used within TaskProvider');
}
return context;
}
โก Advanced React Patternsโ
1. Memoization with useMemoโ
import { useMemo } from 'react';
function TaskList({ tasks }) {
const sortedTasks = useMemo(() => {
return [...tasks].sort((a, b) =>
new Date(b.createdAt) - new Date(a.createdAt)
);
}, [tasks]);
return <div>{/* render sortedTasks */}</div>;
}
2. Callback Optimization with useCallbackโ
import { useCallback } from 'react';
function App() {
const handleTaskAdd = useCallback((text) => {
// Add task logic
}, []);
return <TaskForm onAdd={handleTaskAdd} />;
}
3. useReducer for Complex Stateโ
import { useReducer } from 'react';
const taskReducer = (state, action) => {
switch (action.type) {
case 'ADD_TASK':
return [...state, action.payload];
case 'TOGGLE_TASK':
return state.map(task =>
task.id === action.payload
? { ...task, completed: !task.completed }
: task
);
case 'DELETE_TASK':
return state.filter(task => task.id !== action.payload);
default:
return state;
}
};
function App() {
const [tasks, dispatch] = useReducer(taskReducer, []);
const addTask = (text) => {
dispatch({ type: 'ADD_TASK', payload: { id: Date.now(), text } });
};
return <div>{/* Your JSX */}</div>;
}
4. Custom Hook for API Callsโ
import { useState, useEffect } from 'react';
function useApi(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
// Usage
function ProductList() {
const { data, loading, error } = useApi('/api/products');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{/* Render products */}</div>;
}
๐ Performance Optimizationโ
1. Code Splittingโ
import { lazy, Suspense } from 'react';
const TaskList = lazy(() => import('./components/TaskList'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<TaskList />
</Suspense>
);
}
2. React.memo for Component Memoizationโ
import { memo } from 'react';
const TaskItem = memo(({ task, onToggle, onDelete }) => {
return <li>{/* Task content */}</li>;
}, (prevProps, nextProps) => {
// Only re-render if task changed
return prevProps.task.id === nextProps.task.id &&
prevProps.task.completed === nextProps.task.completed;
});
3. Virtualization for Long Listsโ
npm install react-window
import { FixedSizeList } from 'react-window';
function LargeTaskList({ tasks }) {
return (
<FixedSizeList
height={600}
itemCount={tasks.length}
itemSize={50}
>
{({ index, style }) => (
<div style={style}>
{tasks[index].text}
</div>
)}
</FixedSizeList>
);
}
๐งช Testing React Componentsโ
npm install --save-dev @testing-library/react @testing-library/jest-dom
import { render, screen, fireEvent } from '@testing-library/react';
import TaskForm from './TaskForm';
test('adds a new task', () => {
const handleAdd = jest.fn();
render(<TaskForm onAddTask={handleAdd} />);
const input = screen.getByPlaceholderText(/what needs to be done/i);
const button = screen.getByText(/add task/i);
fireEvent.change(input, { target: { value: 'New task' } });
fireEvent.click(button);
expect(handleAdd).toHaveBeenCalledWith('New task');
});
๐ฆ Popular React Librariesโ
State Managementโ
- Redux Toolkit: Complex state management
- Zustand: Lightweight alternative
- Jotai: Atomic state management
Routingโ
- React Router: Standard routing solution
UI Librariesโ
- Material-UI: Google's Material Design
- Ant Design: Enterprise-grade UI
- Chakra UI: Accessible component library
Form Handlingโ
- React Hook Form: Performant forms
- Formik: Popular form library
๐ Deploymentโ
# Build for production
npm run build
# Test production build locally
npm install -g serve
serve -s build
Deploy to:
- Vercel:
vercel --prod - Netlify: Drag & drop build folder
- GitHub Pages:
npm run deploy
๐ Key Takeawaysโ
- Components: Build reusable, composable pieces
- Hooks: Manage state and side effects elegantly
- Virtual DOM: React efficiently updates the UI
- Unidirectional Data Flow: Makes apps predictable
- Context API: Share state without prop drilling
- Performance: Optimize with memo, useMemo, useCallback
๐ What's Next?โ
- Learn Next.js for server-side rendering
- Explore React Native for mobile apps
- Master TypeScript with React
- Study advanced patterns (HOCs, Render Props)
- Build real-world projects
React's ecosystem is vast and powerful. Keep building, keep learning!
Ready to master React? Check out our React course or follow us on Twitter!
