Tanstack Start

React Core Concepts

React Core Concepts

Now that you understand the foundations, let's dive deep into React's core concepts and build a real application.

Learning Objectives

By the end of this section, you will be able to:

  • Create functional components and pass data with props
  • Manage component state using the useState hook
  • Handle user events and form inputs
  • Use useEffect for side effects and lifecycle management
  • Render lists dynamically and apply conditional rendering
  • Build a complete todo application with CRUD operations
  • Persist data using localStorage

Components: Functional vs Class

Class Components (Legacy)

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

Functional Components (Modern - We'll use these!)

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// Or with arrow function
const Welcome = (props) => {
  return <h1>Hello, {props.name}</h1>;
};

// Or even shorter
const Welcome = (props) => <h1>Hello, {props.name}</h1>;

We focus on functional components because:

  • Simpler syntax
  • Easier to understand
  • Better performance
  • Modern React standard (with Hooks)

Props: Passing Data Down the Component Tree

What are Props?

Props (properties) are how you pass data from parent to child components

Think of props like function arguments:

// Function with arguments
function greet(name) {
  return `Hello, ${name}`;
}
greet('React'); // "Hello, React"

// Component with props
function Greet(props) {
  return <h1>Hello, {props.name}</h1>;
}
<Greet name="React" /> // <h1>Hello, React</h1>

Passing Props

// Parent component
function App() {
  return (
    <div>
      <Welcome name="Alice" age={25} />
      <Welcome name="Bob" age={30} />
    </div>
  );
}

// Child component
function Welcome(props) {
  return (
    <div>
      <h1>Hello, {props.name}</h1>
      <p>Age: {props.age}</p>
    </div>
  );
}

Destructuring Props

// Instead of using props.name, props.age
function Welcome(props) {
  return (
    <div>
      <h1>Hello, {props.name}</h1>
      <p>Age: {props.age}</p>
    </div>
  );
}

// Destructure in parameter
function Welcome({ name, age }) {
  return (
    <div>
      <h1>Hello, {name}</h1>
      <p>Age: {age}</p>
    </div>
  );
}

Props Can Be Anything

// Strings
<Button text="Click me" />

// Numbers
<Counter initial={0} max={10} />

// Booleans
<Task completed={true} />

// Arrays
<List items={['Item 1', 'Item 2']} />

// Objects
<User data={{ name: 'Alice', email: 'alice@example.com' }} />

// Functions
<Button onClick={() => alert('Clicked!')} />

// Components
<Container content={<div>Hello</div>} />

Props are Read-Only

function Welcome(props) {
  // ❌ Wrong - Never modify props
  props.name = 'Changed';

  // ✅ Correct - Props are read-only
  return <h1>Hello, {props.name}</h1>;
}

Rule: All React components must act like pure functions with respect to their props.


State: useState Hook in Depth

What is State?

State is data that changes over time in your component

The difference between props and state:

  • Props: Passed from parent (like function arguments)
  • State: Managed within component (like variables)

Using useState

import { useState } from 'react';

function Counter() {
  // Declare state variable
  const [count, setCount] = useState(0);
  //     ↑        ↑           ↑
  //   current  function   initial
  //   value    to update   value

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

Multiple State Variables

function TaskForm() {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');
  const [priority, setPriority] = useState('medium');
  const [completed, setCompleted] = useState(false);

  return (
    <form>
      <input
        value={title}
        onChange={(e) => setTitle(e.target.value)}
      />
      <textarea
        value={description}
        onChange={(e) => setDescription(e.target.value)}
      />
      <select
        value={priority}
        onChange={(e) => setPriority(e.target.value)}
      >
        <option value="low">Low</option>
        <option value="medium">Medium</option>
        <option value="high">High</option>
      </select>
    </form>
  );
}

State Updates are Asynchronous

function Counter() {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
    console.log(count); // ❌ Still shows old value!
    // State updates are async
  };

  return <button onClick={handleClick}>Count: {count}</button>;
}

Updating State Based on Previous State

// ❌ Wrong - Can miss updates
setCount(count + 1);
setCount(count + 1); // Might not work as expected

// ✅ Correct - Use function form
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1); // Works correctly

State with Objects

function UserProfile() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 0
  });

  // ❌ Wrong - Loses other properties
  const updateName = (name) => {
    setUser({ name: name });
  };

  // ✅ Correct - Spread existing properties
  const updateName = (name) => {
    setUser(prevUser => ({
      ...prevUser,
      name: name
    }));
  };

  return (
    <input
      value={user.name}
      onChange={(e) => updateName(e.target.value)}
    />
  );
}

Event Handling in React

Basic Event Handling

function Button() {
  const handleClick = () => {
    alert('Button clicked!');
  };

  return <button onClick={handleClick}>Click me</button>;
}

// Or inline
function Button() {
  return (
    <button onClick={() => alert('Button clicked!')}>
      Click me
    </button>
  );
}

Common Events

function EventExamples() {
  return (
    <div>
      {/* Click events */}
      <button onClick={() => console.log('Clicked')}>
        Click
      </button>

      {/* Input events */}
      <input
        onChange={(e) => console.log(e.target.value)}
        onFocus={() => console.log('Focused')}
        onBlur={() => console.log('Blurred')}
      />

      {/* Form events */}
      <form onSubmit={(e) => {
        e.preventDefault();
        console.log('Submitted');
      }}>
        <button type="submit">Submit</button>
      </form>

      {/* Mouse events */}
      <div
        onMouseEnter={() => console.log('Mouse entered')}
        onMouseLeave={() => console.log('Mouse left')}
      >
        Hover me
      </div>
    </div>
  );
}

Event Object

function InputExample() {
  const [value, setValue] = useState('');

  const handleChange = (event) => {
    // Event object contains useful information
    console.log(event.target.value);  // Current input value
    console.log(event.target.name);   // Input name attribute
    console.log(event.type);          // Event type (e.g., 'change')

    setValue(event.target.value);
  };

  return (
    <input
      name="username"
      value={value}
      onChange={handleChange}
    />
  );
}

Controlled Components Pattern

What is a Controlled Component?

A form element whose value is controlled by React state

function ControlledInput() {
  const [value, setValue] = useState('');

  // ❌ Uncontrolled - React doesn't know the value
  return <input />;

  // ✅ Controlled - React controls the value
  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
    />
  );
}

Complete Form Example

function TaskForm() {
  const [formData, setFormData] = useState({
    title: '',
    description: '',
    priority: 'medium'
  });

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form data:', formData);
    // Process form data here
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="title"
        value={formData.title}
        onChange={handleChange}
        placeholder="Task title"
      />
      <textarea
        name="description"
        value={formData.description}
        onChange={handleChange}
        placeholder="Description"
      />
      <select
        name="priority"
        value={formData.priority}
        onChange={handleChange}
      >
        <option value="low">Low</option>
        <option value="medium">Medium</option>
        <option value="high">High</option>
      </select>
      <button type="submit">Add Task</button>
    </form>
  );
}

useEffect Hook: Side Effects and Lifecycle

What is useEffect?

useEffect lets you perform side effects in function components:

  • Fetching data
  • Subscriptions
  • Manually changing the DOM
  • Timers
  • localStorage operations

Basic useEffect

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Runs after every render
  useEffect(() => {
    console.log('Component rendered or updated');
    document.title = `Count: ${count}`;
  });

  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

useEffect with Dependencies

function Example() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');

  // Runs only when count changes
  useEffect(() => {
    console.log('Count changed:', count);
  }, [count]);

  // Runs only on mount (once)
  useEffect(() => {
    console.log('Component mounted');
  }, []);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
    </div>
  );
}

useEffect with Cleanup

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // Set up interval
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);

    // Cleanup function
    return () => {
      clearInterval(interval);
      console.log('Timer cleaned up');
    };
  }, []); // Empty array = run once on mount

  return <div>Seconds: {seconds}</div>;
}

localStorage with useEffect

function TaskList() {
  const [tasks, setTasks] = useState([]);

  // Load from localStorage on mount
  useEffect(() => {
    const savedTasks = localStorage.getItem('tasks');
    if (savedTasks) {
      setTasks(JSON.parse(savedTasks));
    }
  }, []);

  // Save to localStorage when tasks change
  useEffect(() => {
    localStorage.setItem('tasks', JSON.stringify(tasks));
  }, [tasks]);

  return (
    <div>
      {tasks.map(task => (
        <div key={task.id}>{task.title}</div>
      ))}
    </div>
  );
}

Rendering Lists with .map()

Basic List Rendering

function TaskList() {
  const tasks = [
    { id: 1, title: 'Learn React' },
    { id: 2, title: 'Build App' },
    { id: 3, title: 'Deploy' }
  ];

  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>{task.title}</li>
      ))}
    </ul>
  );
}

Why Keys are Important

// ❌ Wrong - No key
{tasks.map(task => (
  <li>{task.title}</li>
))}

// ❌ Wrong - Index as key (avoid if list changes)
{tasks.map((task, index) => (
  <li key={index}>{task.title}</li>
))}

// ✅ Correct - Unique ID as key
{tasks.map(task => (
  <li key={task.id}>{task.title}</li>
))}

Why? Keys help React identify which items have changed, been added, or removed. This makes updates efficient.


Complete List Example

function TaskList() {
  const [tasks, setTasks] = useState([
    { id: 1, title: 'Learn React', completed: false },
    { id: 2, title: 'Build App', completed: false },
    { id: 3, title: 'Deploy', completed: true }
  ]);

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

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

  return (
    <ul>
      {tasks.map(task => (
        <li key={task.id}>
          <input
            type="checkbox"
            checked={task.completed}
            onChange={() => toggleTask(task.id)}
          />
          <span style={{
            textDecoration: task.completed ? 'line-through' : 'none'
          }}>
            {task.title}
          </span>
          <button onClick={() => deleteTask(task.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

Conditional Rendering Patterns

If/Else with Variables

function Greeting({ isLoggedIn }) {
  let content;

  if (isLoggedIn) {
    content = <h1>Welcome back!</h1>;
  } else {
    content = <h1>Please sign in</h1>;
  }

  return <div>{content}</div>;
}

Ternary Operator

function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <h1>Welcome back!</h1>
      ) : (
        <h1>Please sign in</h1>
      )}
    </div>
  );
}

Logical && Operator

function TaskList({ tasks }) {
  return (
    <div>
      <h2>Tasks</h2>
      {tasks.length === 0 && (
        <p>No tasks yet. Add one!</p>
      )}
      {tasks.length > 0 && (
        <ul>
          {tasks.map(task => (
            <li key={task.id}>{task.title}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

Early Return

function TaskItem({ task }) {
  if (!task) {
    return <div>No task found</div>;
  }

  if (task.deleted) {
    return null; // Render nothing
  }

  return (
    <div>
      <h3>{task.title}</h3>
      <p>{task.description}</p>
    </div>
  );
}

Live Demo: Building a Simple Counter

Let's build a counter together to see all concepts in action!

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);

  const increment = () => setCount(count + step);
  const decrement = () => setCount(count - step);
  const reset = () => setCount(0);

  return (
    <div>
      <h1>Counter: {count}</h1>

      <div>
        <label>
          Step:
          <input
            type="number"
            value={step}
            onChange={(e) => setStep(Number(e.target.value))}
          />
        </label>
      </div>

      <div>
        <button onClick={decrement}>-{step}</button>
        <button onClick={reset}>Reset</button>
        <button onClick={increment}>+{step}</button>
      </div>

      {count > 10 && (
        <p style={{ color: 'green' }}>Great job counting!</p>
      )}

      {count < 0 && (
        <p style={{ color: 'red' }}>Negative numbers!</p>
      )}
    </div>
  );
}

export default Counter;

🍽️ Lunch Break


Practice Session: Building TaskMaster v1

Project Briefing

TaskMaster v1 Requirements

Core Features:

  • Display list of tasks
  • Add new task with title and description
  • Mark task as complete/incomplete
  • Delete tasks
  • Filter tasks (All, Active, Completed)
  • Edit existing tasks
  • Persist to localStorage
  • Basic styling

Technical Requirements

  • Minimum 3 components
  • Use useState for state management
  • Use useEffect for localStorage
  • Proper event handling
  • Key prop for list items

Suggested Component Structure

App
├── TaskForm (add new task)
├── TaskFilter (filter buttons)
└── TaskList
    └── TaskItem (individual task)

Data Structure

const task = {
  id: 1,                    // unique identifier
  title: 'Learn React',     // task title
  description: 'Study...',  // task description
  completed: false,         // completion status
  createdAt: Date.now()     // timestamp
};

const tasks = [task1, task2, task3];

Building Phase 1

Step 1: Project Setup

# Create React app
npm create vite@latest taskmaster -- --template react
cd taskmaster
npm install
npm run dev

Step 2: Create App Component Structure

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

function App() {
  const [tasks, setTasks] = useState([]);
  const [filter, setFilter] = useState('all');

  return (
    <div className="app">
      <h1>TaskMaster</h1>
      {/* Components will go here */}
    </div>
  );
}

export default App;

Step 3: Create TaskForm Component

// components/TaskForm.jsx
import { useState } from 'react';

function TaskForm({ onAddTask }) {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();

    if (!title.trim()) return;

    const newTask = {
      id: Date.now(),
      title: title.trim(),
      description: description.trim(),
      completed: false,
      createdAt: Date.now()
    };

    onAddTask(newTask);
    setTitle('');
    setDescription('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Task title"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        required
      />
      <textarea
        placeholder="Task description"
        value={description}
        onChange={(e) => setDescription(e.target.value)}
      />
      <button type="submit">Add Task</button>
    </form>
  );
}

export default TaskForm;

Step 4: Create TaskList Component

// components/TaskList.jsx
import TaskItem from './TaskItem';

function TaskList({ tasks, onToggleTask, onDeleteTask, onEditTask }) {
  if (tasks.length === 0) {
    return <p>No tasks yet. Add one above!</p>;
  }

  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 5: Create TaskItem Component

// components/TaskItem.jsx
import { useState } from 'react';

function TaskItem({ task, onToggle, onDelete, onEdit }) {
  const [isEditing, setIsEditing] = useState(false);
  const [editTitle, setEditTitle] = useState(task.title);
  const [editDescription, setEditDescription] = useState(task.description);

  const handleEdit = () => {
    if (editTitle.trim()) {
      onEdit(task.id, {
        title: editTitle.trim(),
        description: editDescription.trim()
      });
      setIsEditing(false);
    }
  };

  if (isEditing) {
    return (
      <li className="task-item editing">
        <input
          type="text"
          value={editTitle}
          onChange={(e) => setEditTitle(e.target.value)}
        />
        <textarea
          value={editDescription}
          onChange={(e) => setEditDescription(e.target.value)}
        />
        <button onClick={handleEdit}>Save</button>
        <button onClick={() => setIsEditing(false)}>Cancel</button>
      </li>
    );
  }

  return (
    <li className={`task-item ${task.completed ? 'completed' : ''}`}>
      <input
        type="checkbox"
        checked={task.completed}
        onChange={() => onToggle(task.id)}
      />
      <div className="task-content">
        <h3>{task.title}</h3>
        {task.description && <p>{task.description}</p>}
      </div>
      <div className="task-actions">
        <button onClick={() => setIsEditing(true)}>Edit</button>
        <button onClick={() => onDelete(task.id)}>Delete</button>
      </div>
    </li>
  );
}

export default TaskItem;

Step 6: Create TaskFilter Component

// components/TaskFilter.jsx
function TaskFilter({ currentFilter, onFilterChange }) {
  const filters = ['all', 'active', 'completed'];

  return (
    <div className="task-filter">
      {filters.map(filter => (
        <button
          key={filter}
          className={currentFilter === filter ? 'active' : ''}
          onClick={() => onFilterChange(filter)}
        >
          {filter.charAt(0).toUpperCase() + filter.slice(1)}
        </button>
      ))}
    </div>
  );
}

export default TaskFilter;

Step 7: Connect Everything in App

// App.jsx
import { useState, useEffect } from 'react';
import TaskForm from './components/TaskForm';
import TaskList from './components/TaskList';
import TaskFilter from './components/TaskFilter';
import './App.css';

function App() {
  const [tasks, setTasks] = useState([]);
  const [filter, setFilter] = useState('all');

  // Load from localStorage on mount
  useEffect(() => {
    const savedTasks = localStorage.getItem('tasks');
    if (savedTasks) {
      setTasks(JSON.parse(savedTasks));
    }
  }, []);

  // Save to localStorage when tasks change
  useEffect(() => {
    localStorage.setItem('tasks', JSON.stringify(tasks));
  }, [tasks]);

  const addTask = (task) => {
    setTasks([...tasks, task]);
  };

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

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

  const editTask = (id, updates) => {
    setTasks(tasks.map(task =>
      task.id === id
        ? { ...task, ...updates }
        : task
    ));
  };

  // Filter tasks based on current filter
  const filteredTasks = tasks.filter(task => {
    if (filter === 'active') return !task.completed;
    if (filter === 'completed') return task.completed;
    return true; // 'all'
  });

  return (
    <div className="app">
      <h1>TaskMaster</h1>
      <TaskForm onAddTask={addTask} />
      <TaskFilter
        currentFilter={filter}
        onFilterChange={setFilter}
      />
      <TaskList
        tasks={filteredTasks}
        onToggleTask={toggleTask}
        onDeleteTask={deleteTask}
        onEditTask={editTask}
      />
      <div className="task-stats">
        <p>Total: {tasks.length}</p>
        <p>Active: {tasks.filter(t => !t.completed).length}</p>
        <p>Completed: {tasks.filter(t => t.completed).length}</p>
      </div>
    </div>
  );
}

export default App;

☕ Break (15 minutes)


Building Phase 2

Tasks to Complete

  1. ✅ Toggle complete/incomplete
  2. ✅ Delete task
  3. ✅ Edit task (inline or modal)
  4. ✅ Filter functionality
  5. ✅ localStorage integration
  6. ✅ CSS styling

Focus Areas:

  • Debug any issues
  • Add validation
  • Improve user experience
  • Add CSS styling
  • Test all features

Basic CSS Styling

/* App.css */
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  background-color: #f5f5f5;
  padding: 20px;
}

.app {
  max-width: 800px;
  margin: 0 auto;
  background: white;
  padding: 30px;
  border-radius: 10px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

h1 {
  color: #333;
  margin-bottom: 30px;
  text-align: center;
}

/* TaskForm */
form {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 30px;
}

input[type="text"],
textarea {
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 5px;
  font-size: 16px;
}

textarea {
  min-height: 80px;
  resize: vertical;
}

button {
  padding: 10px 20px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 16px;
}

button:hover {
  background: #0056b3;
}

/* TaskFilter */
.task-filter {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.task-filter button {
  flex: 1;
  background: #e9ecef;
  color: #333;
}

.task-filter button.active {
  background: #007bff;
  color: white;
}

/* TaskList */
.task-list {
  list-style: none;
}

.task-item {
  display: flex;
  align-items: center;
  gap: 15px;
  padding: 15px;
  background: #f8f9fa;
  border-radius: 5px;
  margin-bottom: 10px;
}

.task-item.completed .task-content h3 {
  text-decoration: line-through;
  color: #999;
}

.task-content {
  flex: 1;
}

.task-content h3 {
  margin-bottom: 5px;
  color: #333;
}

.task-content p {
  color: #666;
  font-size: 14px;
}

.task-actions {
  display: flex;
  gap: 5px;
}

.task-actions button {
  padding: 5px 10px;
  font-size: 14px;
}

/* Task Stats */
.task-stats {
  display: flex;
  justify-content: space-around;
  padding: 20px;
  background: #f8f9fa;
  border-radius: 5px;
  margin-top: 30px;
}

.task-stats p {
  color: #666;
  font-weight: bold;
}

Code Review & Reflection


Common Issues & Solutions

Issue 1: State not updating

// ❌ Wrong
tasks[0].completed = true;

// ✅ Correct
setTasks(tasks.map(task =>
  task.id === targetId
    ? { ...task, completed: true }
    : task
));

Issue 2: Missing keys in lists

// ❌ Wrong
{tasks.map(task => <li>{task.title}</li>)}

// ✅ Correct
{tasks.map(task => <li key={task.id}>{task.title}</li>)}

Issue 3: Form not preventing default

// ❌ Wrong
const handleSubmit = () => {
  addTask(newTask);
};

// ✅ Correct
const handleSubmit = (e) => {
  e.preventDefault();
  addTask(newTask);
};

Issue 4: Not validating input

// ❌ Wrong
onAddTask({ title, description });

// ✅ Correct
if (!title.trim()) return;
onAddTask({
  title: title.trim(),
  description: description.trim()
});

Code Quality Checklist

✅ Components are properly organized ✅ Functions have descriptive names ✅ State is updated immutably ✅ Event handlers prevent default when needed ✅ Forms validate input ✅ Lists have proper keys ✅ Code is readable and well-formatted ✅ localStorage works correctly ✅ No console errors


On this page

React Core ConceptsLearning ObjectivesComponents: Functional vs ClassClass Components (Legacy)Functional Components (Modern - We'll use these!)Props: Passing Data Down the Component TreeWhat are Props?Passing PropsDestructuring PropsProps Can Be AnythingProps are Read-OnlyState: useState Hook in DepthWhat is State?Using useStateMultiple State VariablesState Updates are AsynchronousUpdating State Based on Previous StateState with ObjectsEvent Handling in ReactBasic Event HandlingCommon EventsEvent ObjectControlled Components PatternWhat is a Controlled Component?Complete Form ExampleuseEffect Hook: Side Effects and LifecycleWhat is useEffect?Basic useEffectuseEffect with DependenciesuseEffect with CleanuplocalStorage with useEffectRendering Lists with .map()Basic List RenderingWhy Keys are ImportantComplete List ExampleConditional Rendering PatternsIf/Else with VariablesTernary OperatorLogical && OperatorEarly ReturnLive Demo: Building a Simple Counter🍽️ Lunch BreakPractice Session: Building TaskMaster v1Project BriefingTaskMaster v1 RequirementsTechnical RequirementsSuggested Component StructureData StructureBuilding Phase 1Step 1: Project SetupStep 2: Create App Component StructureStep 3: Create TaskForm ComponentStep 4: Create TaskList ComponentStep 5: Create TaskItem ComponentStep 6: Create TaskFilter ComponentStep 7: Connect Everything in App☕ Break (15 minutes)Building Phase 2Tasks to CompleteFocus Areas:Basic CSS StylingCode Review & ReflectionCommon Issues & SolutionsCode Quality Checklist