← Back to Home

Stage 4: React Added

Core Concepts: Components & State

  • Components Instead of one huge HTML file, we break the UI into reusable pieces (like <TodoItem />).
  • Declarative UI (JSX) Instead of step-by-step DOM manipulation, we tell React what the UI should look like based on the data.
  • Reactive State (useState) The data (tasks) is the source of truth. When the data changes, React automatically updates the DOM to match. No manual syncing!
  • Props We pass data and functions down from the App component to the TodoItem component via "props".
Notice the difference: In Stage 3, we had to manually count checked items and update the counter text every time something changed. In React, we just calculate completedCount from the state, and React handles the rest!
import { useState } from 'react';
import TodoItem from './components/TodoItem';

export default function App() {
  // Single source of truth for our tasks
  const [tasks, setTasks] = useState([
    { id: 1, text: 'Learn HTML structure', done: true },
    { id: 2, text: 'Add CSS styling', done: true },
    { id: 3, text: 'Build React components', done: false }
  ]);
  
  const [inputValue, setInputValue] = useState('');

  // Derived state: no manual DOM querying needed
  const completedCount = tasks.filter(t => t.done).length;

  const handleAddTask = (e) => {
    e.preventDefault();
    if (!inputValue.trim()) return;
    setTasks([...tasks, { id: Date.now(), text: inputValue, done: false }]);
    setInputValue('');
  };

  const toggleTask = (id) => {
    setTasks(tasks.map(task => 
      task.id === id ? { ...task, done: !task.done } : task
    ));
  };

  const deleteTask = (id) => {
    setTasks(tasks.filter(task => task.id !== id));
  };

  return (
    <div className="todo-app">
      <div className="todo-header">
        <h1 className="todo-title">My Tasks</h1>
        <span className="todo-counter">
          {completedCount} of {tasks.length} tasks completed
        </span>
      </div>

      <form className="todo-form" onSubmit={handleAddTask}>
        <input 
          className="todo-input" 
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
        />
        <button type="submit" className="btn btn-primary">Add</button>
      </form>

      <ul className="todo-list">
        {tasks.map(task => (
          <TodoItem 
            key={task.id} 
            task={task} 
            onToggle={toggleTask} 
            onDelete={deleteTask} 
          />
        ))}
      </ul>
    </div>
  );
}