Week 8: React Introduction

Component-Based Architecture & Modern Web Development

INFO 153A/253A - Front-End Web Architecture

UC Berkeley School of Information

October 13, 2025

Part 1: Prep Work Recap

Understanding React's Core Philosophy

From Your Prep: What is React?

"A JavaScript library created and maintained by Facebook for building user interfaces"

  • React is a UI library, not a framework: It only handles the view layer, unlike Angular which provides everything
  • Component-based architecture: Think of your UI as LEGO blocks that snap together
  • Declarative approach: You describe what you want, React figures out how to make it happen
  • Virtual DOM for performance: React keeps a JavaScript representation of the UI for efficient updates
  • Created by Facebook in 2013: Built to solve their massive UI complexity problems

Why React Exists

Traditional Approach (Week 7)

  • Scattered event listeners
  • Manual DOM updates
  • Global variables everywhere
  • HTML, CSS, JS separated

React Approach

  • Components handle their events
  • Automatic UI updates
  • Encapsulated state
  • Components contain everything
  • The problem React solves: Managing complex UIs becomes exponentially harder with traditional methods
  • Facebook's insight: UI = f(state) - the UI should be a pure function of application state
  • Component isolation: Changes in one component don't break others
  • Predictable updates: When state changes, React automatically updates the UI
  • Developer experience: Better debugging, hot reloading, and clear data flow

Prerequisites You've Already Mastered

✅ You're Ready for React!

Weeks 1-7 have prepared you perfectly

  • JavaScript fundamentals (Week 4 & 6): Variables, arrays, objects, functions - all essential for React
  • DOM manipulation (Week 7): You understand what React is abstracting away
  • Array methods (Week 6): map(), filter(), reduce() are React's best friends
  • Arrow functions (Week 6): The standard way to write functions in React
  • Modern CSS (Week 3 & 5): Your styling knowledge transfers directly
  • Event handling (Week 7): React events work similarly but better

The Component Mental Model

Everything is a Component

App
Header
TaskList
TaskItem
TaskItem
Footer
  • Components are functions: They take inputs (props) and return UI (JSX)
  • Composability: Small components combine to create complex UIs
  • Reusability: Write once, use many times with different data
  • Isolation: Each component manages its own piece of the UI
  • Think in components: Start seeing every UI as a tree of components

React's Virtual DOM Magic

// What you write (declarative)
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(prevCount => prevCount + 1)}>
      Clicked {count} times
    </button>
  );
}

// What React does for you (imperative)
// 1. Compares virtual DOM trees
// 2. Finds minimal changes needed
// 3. Updates only what changed in real DOM
  • Virtual DOM is a JavaScript object: Lightweight copy of the real DOM structure
  • Diffing algorithm: React compares old and new virtual DOM trees
  • Minimal updates: Only changed elements are updated in the real DOM
  • Performance win: Batch updates and avoid expensive DOM operations
  • You never touch the DOM: React handles all DOM manipulation for you

Part 2: JSX - JavaScript in Disguise

Writing HTML that's Actually JavaScript

JSX = JavaScript XML

Syntactic sugar that makes React components readable

  • JSX is not HTML: It's JavaScript that looks like HTML for developer convenience
  • Transpilation required: Babel converts JSX to React.createElement() calls
  • Why use JSX: Writing React.createElement() manually would be a nightmare
  • It's optional but universal: You could write React without it, but nobody does
  • Powerful advantage: Full JavaScript power inside your markup

Your First JSX

// What you write (JSX)
function Welcome() {
  return (
    <div className="container">
      <h1>Hello React!</h1>
      <p>Welcome to component-based development</p>
    </div>
  );
}
// What Babel produces (pure JavaScript)
function Welcome() {
  return React.createElement('div', { className: 'container' },
    React.createElement('h1', null, 'Hello React!'),
    React.createElement('p', null, 'Welcome to component-based development')
  );
}
  • JSX makes React human-readable: Imagine writing complex UIs with createElement!
  • Return statement wrapping: Parentheses help with multi-line JSX
  • One parent element rule: Every return must have a single root element
  • Babel does the heavy lifting: Transformation happens automatically in build process

JSX Rules and Gotchas

❌ HTML Attributes

class="container"
for="email"
onclick="handleClick()"
style="color: blue;"

✅ JSX Attributes

<div className="container">
  <label htmlFor="email">Email</label>
  <button onClick={handleClick}>Click</button>
  <span style={{color: 'blue'}}>Text</span>
</div>
  • className not class: 'class' is a reserved word in JavaScript
  • camelCase events: onClick, onChange, onSubmit match JavaScript conventions
  • Style is an object: Double curly braces - outer for JSX, inner for object
  • Self-closing tags required: <img /> not <img>
  • JavaScript in curly braces: Any JS expression can go in {}

Dynamic JSX with JavaScript

function TaskList() {
  const tasks = ['Learn React', 'Build App', 'Deploy'];
  const isLoggedIn = true;

  return (
    <div>
      <h2>{isLoggedIn ? 'Your Tasks' : 'Please Login'}</h2>
      <ul>
        {tasks.map(task => (
          <li key={task}>{task}</li>
        ))}
      </ul>
      <p>You have {tasks.length} tasks</p>
    </div>
  );
}
  • Curly braces are the bridge: They let you escape from JSX into JavaScript
  • Any expression works: Variables, ternary operators, function calls, array methods
  • map() is your best friend: The primary way to render lists in React
  • Conditional rendering: Ternary operators and && for showing/hiding elements
  • Key prop for lists: React needs keys to track list items efficiently

JSX Fragments and Returns

// Problem: Can't return multiple elements
function Bad() {
  return (
    <h1>Title</h1>
    <p>Paragraph</p> // Error!
  );
}

// Solution 1: Wrapper div
function WithDiv() {
  return (
    <div>
      <h1>Title</h1>
      <p>Paragraph</p>
    </div>
  );
}

// Solution 2: React Fragment (no extra DOM node)
function WithFragment() {
  return (
    <>
      <h1>Title</h1>
      <p>Paragraph</p>
    </>
  );
}
  • Single parent rule: JSX must return one root element
  • Fragments avoid wrapper divs: <></> groups elements without adding to DOM
  • Cleaner HTML output: Fragments prevent "div soup" in your markup
  • When to use each: Use div when you need styling, Fragment when you don't
  • Long form available: <React.Fragment> can take a key prop

Part 3: Components and Props

Building Blocks of React Applications

Component Definition

A function that returns JSX

  • Components are just functions: They take props as arguments and return JSX
  • Naming convention: Always PascalCase (TaskList not taskList)
  • Pure functions ideally: Same props should always produce same output
  • Composable by design: Components can contain other components
  • Single responsibility: Each component should do one thing well

Creating Your First Component

// Task component - receives props and returns JSX
function Task(props) {
  return (
    <div className="task">
      <h3>{props.title}</h3>
      <p>{props.description}</p>
      <span className="priority">{props.priority}</span>
    </div>
  );
}

// Using the component
function App() {
  return (
    <div>
      <Task
        title="Learn React"
        description="Complete Week 8 materials"
        priority="High"
      />
      <Task
        title="Build Project"
        description="Start Assignment 2"
        priority="Medium"
      />
    </div>
  );
}
  • Props are function arguments: Passed as attributes when using the component
  • Props are read-only: Never modify props inside a component
  • Reusability achieved: Same component, different data
  • Parent-child relationship: App is parent, Task is child
  • Data flows down: Parents pass props to children, not vice versa

Destructuring Props

// Without destructuring (repetitive)
function TaskWithProps(props) {
  return (
    <div>
      <h3>{props.title}</h3>
      <p>{props.description}</p>
      <span>{props.priority}</span>
    </div>
  );
}

// With destructuring (cleaner)
function TaskWithDestructuring({ title, description, priority }) {
  return (
    <div>
      <h3>{title}</h3>
      <p>{description}</p>
      <span>{priority}</span>
    </div>
  );
}

// With default values
function TaskWithDefaults({ title = 'Untitled', description = '', priority = 'Low' }) {
  return (
    <div>
      <h3>{title}</h3>
      <p>{description}</p>
      <span>{priority}</span>
    </div>
  );
}
  • Destructuring improves readability: No more props.props.props everywhere
  • Parameters are clear: You can see what props a component expects
  • Default values prevent errors: Handle missing props gracefully
  • Modern JavaScript feature: Same destructuring from Week 6
  • Industry standard: Most React code uses destructured props

Props Types and Children

// Props can be any JavaScript type
function UserCard({
  name,           // string
  age,            // number
  isActive,       // boolean
  hobbies,        // array
  address,        // object
  onDelete,       // function
  children        // special prop for nested content
}) {
  return (
    <div className="user-card">
      <h2>{name}</h2>
      <p>Age: {age}</p>
      {isActive && <span className="active">Active</span>}
      <ul>
        {hobbies.map(hobby => <li key={hobby}>{hobby}</li>)}
      </ul>
      <button onClick={() => onDelete(name)}>Delete</button>
      <div className="card-content">
        {children}
      </div>
    </div>
  );
}

// Using children prop
<UserCard name="Alice" age={25}>
  <p>This appears as children!</p>
  <button>Extra button</button>
</UserCard>
  • Props are flexible: Any JavaScript value can be a prop
  • Functions as props: Pass callbacks for child-to-parent communication
  • Children is special: Whatever goes between component tags becomes children
  • Conditional rendering: Use && operator for show/hide logic
  • Type checking available: PropTypes or TypeScript for validation (optional)

Component Composition Patterns

// Composing components together
function TaskManager() {
  const tasks = [
    { id: 1, title: 'Learn React', status: 'active' },
    { id: 2, title: 'Build App', status: 'completed' }
  ];

  return (
    <div className="task-manager">
      <Header title="My Tasks" count={tasks.length} />
      <TaskFilter />
      <TaskList tasks={tasks} />
      <AddTaskForm />
      <Footer />
    </div>
  );
}

// Each component is focused and reusable
function TaskList({ tasks }) {
  return (
    <div className="task-list">
      {tasks.map(task => (
        <TaskItem key={task.id} {...task} />
      ))}
    </div>
  );
}

function TaskItem({ title, status }) {
  return (
    <div className={`task-item ${status}`}>
      <span>{title}</span>
      <TaskStatus status={status} />
    </div>
  );
}
  • Composition over inheritance: React favors combining components
  • Single responsibility: Each component has one clear job
  • Props spreading: {...task} passes all properties as props
  • Component hierarchy: Clear parent-child relationships
  • Reusability maximized: TaskItem works for any task data

Part 4: React with Vite

Modern React Development Environment

About Your Prep Work

Prep videos use Create React App - we're using Vite (the industry standard today)

  • Create React App deprecated: Was official tool, now replaced by Vite
  • Vite is the modern choice: Faster, lighter, actively maintained
  • Same React concepts: Everything you learned in prep still applies
  • Why the switch: Industry moved to Vite in 2023-2024
  • Both are build tools: Just different ways to run the same React code
  • Vite benefits: Instant dev server, faster builds, modern tooling

Creating Your First Vite + React App

# Create new Vite project with React template
npm create vite@latest my-portfolio -- --template react

# Navigate to project
cd my-portfolio

# Install dependencies
npm install

# Start development server
npm run dev

# Your app is now running at http://localhost:5173

What Just Happened?

  • Downloaded Vite React template
  • Created minimal project structure
  • Installed React and Vite dependencies
  • Started instant dev server (no bundling wait!)
  • Vite scaffolding: Creates project template instantly
  • Two-step setup: Create, then npm install (installs dependencies)
  • npm run dev: Vite's dev command (not npm start)
  • Port 5173 default: Different from CRA's 3000
  • Instant startup: Dev server ready in milliseconds vs seconds
  • Hot Module Replacement: Changes reflect instantly

Vite Project Structure

my-portfolio/
├── node_modules/       # Dependencies (never edit)
├── public/            # Static assets (images, fonts)
├── src/              # Your code goes here
│   ├── App.jsx        # Root component (note .jsx!)
│   ├── App.css        # App styles
│   ├── main.jsx       # Entry point (not index.js!)
│   └── index.css      # Global styles
├── index.html         # Root HTML (in root, not public!)
├── package.json       # Dependencies & scripts
├── vite.config.js     # Vite configuration
└── .gitignore        
  • index.html in root: Different from CRA (public folder)
  • main.jsx not index.js: Vite's entry point naming convention
  • .jsx extension standard: Makes React files explicit (optional but recommended)
  • src/ is your workspace: All React components and code
  • public/ for static files: Images, fonts that don't need processing
  • vite.config.js for setup: Usually don't need to touch this

Understanding main.jsx

// src/main.jsx - The bridge between React and the DOM
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App.jsx'

// Find the root div in index.html
ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <App />
    </React.StrictMode>,
)
  • Nearly identical to CRA: Just different filename (main.jsx vs index.js)
  • This file rarely changes: It's just the connection point
  • ReactDOM handles browser rendering: React core is platform agnostic
  • StrictMode helps development: Catches common mistakes and warnings
  • App component is your starting point: Everything flows from App
  • Chained syntax: Vite template uses method chaining (same result)

Essential NPM Scripts

// package.json scripts
{
    "scripts": {
        "dev": "vite",                    // Development server
        "build": "vite build",            // Production build
        "preview": "vite preview"         // Preview production build locally
    }
}

Key Difference from CRA

Use npm run dev (not npm start)

  • npm run dev for development: Your main command (note: not npm start!)
  • npm run build for production: Creates optimized files in dist/ folder
  • npm run preview for testing: Test production build locally before deployment
  • dist/ folder is deployment-ready: Upload to any static host
  • No eject needed: Vite config is already accessible in vite.config.js
  • Lightning fast builds: Vite builds are significantly faster than CRA

Part 5: From Assignment 1 to React

Transforming Static HTML to Dynamic Components

Mental Model Shift

From manipulating DOM to declaring components

  • Assignment 1 was static: HTML and CSS with no interactivity
  • Assignment 2 adds React: Same design, now interactive and dynamic
  • Components replace HTML blocks: Each section becomes a component
  • State replaces hardcoded data: Tasks live in JavaScript, not HTML
  • Props enable customization: Reusable components with different data

Identifying Components in Your Task Manager

Assignment 1 Structure

<header>
    <h1>Task Manager</h1>
</header>
<main>
    <div class="filters">...</div>
    <div class="task-list">
        <div class="task">...</div>
        <div class="task">...</div>
    </div>
    <form class="add-task">...</form>
</main>

React Components

function App() {
    return (
        <>
            <Header />
            <Filters />
            <TaskList tasks={tasks} />
            <AddTaskForm onAdd={addTask} />
        </>
    );
}
  • Natural boundaries: Each visual section becomes a component
  • Repeated elements: Task cards are perfect for components
  • Interactive elements: Forms and buttons need event handlers
  • Data flow planning: Where will task data live? (App state)
  • CSS mostly unchanged: Your styles transfer directly

Static to Dynamic Data

// Before: Hardcoded HTML tasks
// After: JavaScript array of task objects
import { useState } from 'react';

function App() {
    // Tasks are now data, not HTML
    const [tasks, setTasks] = useState([
        {
            id: 1,
            title: "Complete Assignment 2",
            priority: "high",
            completed: false,
            dueDate: "2025-10-31"
        },
        {
            id: 2,
            title: "Study for Midterm",
            priority: "medium",
            completed: false,
            dueDate: "2025-11-15"
        }
    ]);

    return (
        <div className="app">
            <TaskList tasks={tasks} />
        </div>
    );
}
  • Data drives the UI: Changes to tasks array automatically update display
  • useState manages data: React tracks changes and re-renders
  • No more DOM manipulation: Just update state, React handles the rest
  • Centralized data: All tasks in one place, easy to manage
  • Prepared for backends: Easy to replace with API data later

Adding Interactivity

function TaskItem({ task, onToggle, onDelete }) {
  return (
    <div className="task-item">
      <input
        type="checkbox"
        checked={task.completed}
        onChange={() => onToggle(task.id)}
      />
      <span className={task.completed ? 'completed' : ''}>
        {task.title}
      </span>
      <button onClick={() => onDelete(task.id)}>
        Delete
      </button>
    </div>
  );
}

function TaskList({ tasks, onToggle, onDelete }) {
  return (
    <div className="task-list">
      {tasks.map(task => (
        <TaskItem
          key={task.id}
          task={task}
          onToggle={onToggle}
          onDelete={onDelete}
        />
      ))}
    </div>
  );
}
  • Event handlers replace DOM listeners: onClick, onChange built into JSX
  • Props for communication: Child components notify parents of events
  • Controlled components: Form inputs controlled by React state
  • Declarative updates: Describe the UI for each state
  • No querySelector needed: React knows which element was clicked

Assignment 2 Preview

Your React Task Manager Will Include:

  • Add new tasks with form submission
  • Mark tasks as complete/incomplete
  • Delete tasks
  • Filter tasks (all/active/completed)
  • Persist data with localStorage
  • Polish with animations and transitions
  • Build on Assignment 1: Reuse your CSS and design
  • Focus on React concepts: State, props, events, effects
  • Incremental development: Start simple, add features gradually
  • Best practices enforced: Component structure, naming, organization
  • Real-world patterns: Forms, lists, filtering - common React tasks

Key Takeaways

React Fundamentals Covered Today

  • ✅ React is a library for building UIs with components
  • ✅ JSX lets us write HTML-like syntax in JavaScript
  • ✅ Components are functions that return JSX
  • ✅ Props pass data from parent to child components
  • ✅ Vite provides modern, fast React development environment
  • ✅ Your Assignment 1 design becomes React components
  • Mental model shift: Think in components, not DOM manipulation
  • Declarative over imperative: Describe what you want, not how to get it
  • Data flows down: Parents pass props to children
  • Events bubble up: Children notify parents through callbacks
  • Start Assignment 2 early: React concepts need practice to solidify
  • Resources available: Office hours, documentation, course videos