01-react-fundamentals.md

Module 01: React Fundamentals β€” From Zero to Building UIs

Estimated Time: 15-20 hours Difficulty: 🟒 Beginner Prerequisites: HTML, CSS, JavaScript (ES6+) Interview Weight: ⭐⭐⭐⭐⭐ (Most frequently asked topics)


πŸ“‹ Table of Contents

  1. What is React and Why Does It Exist?
  2. Setting Up Your First React App
  3. JSX: The Heart of React's Syntax
  4. Components: The Building Blocks
  5. Props: Passing Data Around
  6. State: Making Things Interactive
  7. Event Handling in React
  8. Conditional Rendering
  9. Rendering Lists and the Key Prop
  10. Forms and Controlled Components
  11. Component Composition vs Inheritance
  12. The Virtual DOM β€” How React Actually Works
  13. Common Mistakes & Gotchas
  14. Practice Problems
  15. Interview Questions
  16. "What's the Output?" Challenges

1. What is React and Why Does It Exist?

The Problem Before React

Imagine you're building Gmail. The inbox has hundreds of emails. When a new email arrives, you need to:

  1. Add a row to the email list
  2. Update the unread count badge
  3. Update the folder counts
  4. Maybe show a notification
  5. If the email is from a starred contact, highlight it

In vanilla JavaScript, you'd manually find each DOM element and update it:

// The old way β€” manual DOM manipulation document.getElementById('email-list').innerHTML += newEmailHTML; document.getElementById('unread-count').textContent = count + 1; document.getElementById('inbox-count').textContent = inboxTotal + 1; // ... and so on for every piece of UI that needs updating // But wait β€” what if the user has a filter active? // What if they're in a different folder? // What if two emails arrive at the same time? // This gets VERY complicated VERY fast.

The problem: keeping the UI in sync with data is incredibly hard. As your app grows, tracking what needs to update when data changes becomes a nightmare. Bugs appear because you forgot to update one element, or updated it in the wrong order, or two updates conflict.

React's Big Idea

React's solution is elegant and simple:

"Don't manually update the DOM. Instead, describe what the UI should look like for any given state, and let React figure out how to get there."

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Traditional (Imperative): β”‚ β”‚ β”‚ β”‚ "Step 1: Find the list element" β”‚ β”‚ "Step 2: Create a new list item" β”‚ β”‚ "Step 3: Set its text to 'New Email'" β”‚ β”‚ "Step 4: Append it to the list" β”‚ β”‚ "Step 5: Find the counter and increment it" β”‚ β”‚ β”‚ β”‚ React (Declarative): β”‚ β”‚ β”‚ β”‚ "Here are my 47 emails. Show them in a list. β”‚ β”‚ The unread count is 5. Show that too." β”‚ β”‚ β”‚ β”‚ React figures out the steps automatically. β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Real-World Analogy

Think of it like ordering at a restaurant:

  • Imperative (vanilla JS): "Go to the kitchen. Take a pan. Put it on the stove. Turn the heat to medium. Crack two eggs. Wait 3 minutes. Flip them..." β€” You tell the chef every single step.

  • Declarative (React): "I'd like two sunny-side-up eggs, please." β€” You describe what you want, and the chef figures out how to make it.

What React Actually Is

React is a JavaScript library (not a framework!) for building user interfaces.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Library vs Framework β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ β”‚ β”‚ Framework (Angular, Vue): β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Routing ┃ State ┃ HTTP β”‚ β”‚ β”‚ β”‚ Forms ┃ DI ┃ Testing β”‚ β”‚ β”‚ β”‚ ───────YOUR CODE────────── β”‚ β”‚ β”‚ β”‚ CLI ┃ Build ┃ More... β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ The framework controls your code. β”‚ β”‚ β”‚ β”‚ Library (React): β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ ───────YOUR CODE────────── β”‚ β”‚ β”‚ β”‚ ┃ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ React β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ (UI) β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ Your code controls React. β”‚ β”‚ You pick your own router, state mgmt, etc. β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Facts About React

FactDetail
Created byJordan Walke at Facebook (2011 internally, 2013 open-sourced)
Current versionReact 18+ (as of 2024)
TypeUI Library
LanguageJavaScript (with JSX syntax extension)
ParadigmDeclarative, Component-based
First major userFacebook's News Feed, then Instagram
Used byFacebook, Instagram, Netflix, Airbnb, Uber, Twitter, Reddit, Discord
LicenseMIT

🏒 Why Companies Use React

  1. Huge ecosystem β€” Libraries for everything (routing, state, forms, animations)
  2. Large talent pool β€” Easy to hire React developers
  3. React Native β€” Share code between web and mobile
  4. Meta (Facebook) backing β€” Well-maintained, not going anywhere
  5. Performance β€” Virtual DOM makes updates efficient
  6. Flexibility β€” Not opinionated, fits different architectures

2. Setting Up Your First React App

Option 1: Vite (Recommended in 2024+)

Vite is the modern, fast way to start a React project:

# Create a new React project with Vite npm create vite@latest my-react-app -- --template react # Navigate into the project cd my-react-app # Install dependencies npm install # Start the development server npm run dev

Option 2: Create React App (Legacy β€” still works but slower)

npx create-react-app my-react-app cd my-react-app npm start

⚠️ Note: Create React App (CRA) is no longer recommended by the React team. Use Vite for new projects. CRA is slow to build, slow to start, and no longer actively maintained.

Understanding the Project Structure

After creating a Vite + React project, you'll see:

my-react-app/ β”œβ”€β”€ node_modules/ # Dependencies (don't touch) β”œβ”€β”€ public/ # Static assets (favicon, etc.) β”œβ”€β”€ src/ # YOUR CODE GOES HERE β”‚ β”œβ”€β”€ App.jsx # Main application component β”‚ β”œβ”€β”€ App.css # Styles for App component β”‚ β”œβ”€β”€ main.jsx # Entry point β€” renders App into the DOM β”‚ β”œβ”€β”€ index.css # Global styles β”‚ └── assets/ # Images, fonts, etc. β”œβ”€β”€ index.html # The single HTML page β”œβ”€β”€ package.json # Project config and dependencies β”œβ”€β”€ vite.config.js # Vite configuration └── README.md

The Entry Point: main.jsx

// src/main.jsx import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App.jsx'; import './index.css'; // This is where React "enters" the DOM // It finds the element with id="root" in index.html // and renders our entire React app inside it const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <App /> </React.StrictMode> );

πŸ” Under the Hood: What Happens When Your App Starts

1. Browser loads index.html └── index.html has <div id="root"></div> and a <script> tag 2. The <script> tag loads main.jsx (bundled by Vite) └── main.jsx imports React and ReactDOM 3. ReactDOM.createRoot() creates a React root └── This tells React "manage everything inside this DOM element" 4. root.render(<App />) tells React to: a. Call the App function b. Get back a tree of React elements (virtual DOM) c. Convert that tree into real DOM nodes d. Insert them into the #root div 5. The page is now visible! └── React is now "in charge" of the #root div └── Any changes go through React, not direct DOM manipulation

3. JSX: The Heart of React's Syntax

What is JSX?

JSX stands for JavaScript XML. It's a syntax extension that lets you write HTML-like code inside JavaScript.

// This is JSX const element = <h1>Hello, world!</h1>; // It looks like HTML, but it's actually JavaScript // Specifically, it's syntactic sugar for a function call

πŸ” Under the Hood: What JSX Compiles To

JSX is NOT valid JavaScript. It gets compiled (by Babel or SWC) into regular function calls:

// What you write (JSX): const element = <h1 className="greeting">Hello, world!</h1>; // What the compiler produces (JavaScript): const element = React.createElement( 'h1', // type { className: 'greeting' }, // props 'Hello, world!' // children ); // In React 17+, the new JSX transform produces: import { jsx as _jsx } from 'react/jsx-runtime'; const element = _jsx('h1', { className: 'greeting', children: 'Hello, world!' });

What React.createElement Returns

React.createElement returns a plain JavaScript object called a React element:

// The React element object looks like this: { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' }, key: null, ref: null, // ... some internal React properties } // This is NOT a DOM element! // It's a lightweight description of what the DOM should look like. // React uses these objects to build the Virtual DOM.

πŸ’‘ Key Insight: JSX β†’ React.createElement() β†’ Plain Object β†’ React uses it to update real DOM. JSX is just a developer-friendly way to describe UI structure.

JSX Rules

Rule 1: Must Return a Single Root Element

// ❌ WRONG β€” Two root elements function App() { return ( <h1>Title</h1> <p>Paragraph</p> ); } // βœ… CORRECT β€” Wrapped in a div function App() { return ( <div> <h1>Title</h1> <p>Paragraph</p> </div> ); } // βœ… BETTER β€” Use a Fragment (no extra DOM node) function App() { return ( <> <h1>Title</h1> <p>Paragraph</p> </> ); } // The <> </> is shorthand for <React.Fragment></React.Fragment>

πŸ” Why single root? Because a function can only return one value. React.createElement() creates one object. You can't return two objects from a function without wrapping them.

Rule 2: Close All Tags

// In HTML, some tags are self-closing: // <img src="photo.jpg"> // <br> // <input type="text"> // In JSX, ALL tags must be closed: <img src="photo.jpg" /> <br /> <input type="text" /> // ❌ <img src="photo.jpg"> β€” Error in JSX!

Rule 3: Use camelCase for HTML Attributes

// HTML β†’ JSX // class β†’ className // for β†’ htmlFor // onclick β†’ onClick // tabindex β†’ tabIndex // readonly β†’ readOnly // maxlength β†’ maxLength // style="..." β†’ style={{...}} <label htmlFor="name" className="form-label"> <input id="name" type="text" tabIndex={1} readOnly maxLength={50} onClick={handleClick} style={{ color: 'red', fontSize: '16px' }} /> </label>

πŸ” Why camelCase? Because JSX compiles to JavaScript objects. In JavaScript, class and for are reserved keywords. className and htmlFor avoid this conflict.

Rule 4: JavaScript Expressions in Curly Braces

function Greeting() { const name = "Sarah"; const age = 28; const isLoggedIn = true; return ( <div> {/* Variables */} <h1>Hello, {name}!</h1> {/* Expressions */} <p>You are {age * 12} months old.</p> {/* Ternary operators */} <p>{isLoggedIn ? 'Welcome back!' : 'Please log in.'}</p> {/* Function calls */} <p>Name in uppercase: {name.toUpperCase()}</p> {/* Template literals (still need curly braces) */} <p>{`${name} is ${age} years old`}</p> {/* ❌ You CANNOT use statements (if/else, for, while) inside JSX */} {/* This won't work: {if (isLoggedIn) { return 'Hi' }} */} </div> ); }

πŸ’‘ Remember: Inside {} in JSX, you can put any JavaScript expression (something that produces a value), but NOT statements (if/else, for, switch, etc.).

JSX Style Attribute

In HTML, style is a string. In JSX, style is a JavaScript object:

// HTML: // <div style="background-color: blue; font-size: 16px; margin-top: 20px;"> // JSX: <div style={{ backgroundColor: 'blue', // camelCase, not kebab-case fontSize: '16px', // or fontSize: 16 (number = pixels) marginTop: '20px', // Note: double curly braces {{ }} // Outer {} = "this is a JS expression" // Inner {} = "this is a JS object" }}>

Comments in JSX

function App() { return ( <div> {/* This is a JSX comment */} <h1>Hello</h1> {/* Multi-line JSX comment */} {// Single-line comment (must be followed by newline before }) } </div> ); }

4. Components: The Building Blocks

What is a Component?

A component is a reusable piece of UI. Think of components like LEGO bricks β€” you build small bricks and snap them together to create complex structures.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ App β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Header β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Logo β”‚ β”‚ Nav β”‚ β”‚ UserMenu β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Main Content β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ Card β”‚ β”‚ Card β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚Image β”‚ β”‚ β”‚ β”‚Image β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Title β”‚ β”‚ Title β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Button β”‚ β”‚ Button β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Footer β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Function Components (Modern Standard)

// A component is just a function that returns JSX function Welcome() { return <h1>Hello, React!</h1>; } // Arrow function syntax (equally valid) const Welcome = () => { return <h1>Hello, React!</h1>; }; // Arrow function with implicit return (for simple components) const Welcome = () => <h1>Hello, React!</h1>;

Class Components (Legacy β€” Still Important for Interviews)

import React, { Component } from 'react'; class Welcome extends Component { render() { return <h1>Hello, React!</h1>; } }

πŸ“Š Function vs Class Components

FeatureFunction ComponentsClass Components
SyntaxSimple functionES6 class extending React.Component
StateuseState hookthis.state + this.setState()
LifecycleuseEffect hookcomponentDidMount, componentDidUpdate, etc.
this keywordNot neededRequired (source of bugs)
PerformanceSlightly better (no class overhead)Slightly worse
Code lengthShorterLonger
Modern Reactβœ… Recommended⚠️ Legacy (still supported)
Hooks supportβœ… Yes❌ No

🎯 Interview Tip: You should know both syntaxes. Interviewers might ask you to convert between them, or you might encounter class components in legacy codebases.

Component Naming Rules

// βœ… Components MUST start with an uppercase letter function UserProfile() { return <div>Profile</div>; } function MyButton() { return <button>Click me</button>; } // ❌ Lowercase = treated as HTML element function userProfile() { return <div>Profile</div>; } // React will try to render <userprofile> as an HTML tag, not your component!

πŸ” Why uppercase? React uses the case to distinguish between DOM elements (<div>, <span>) and custom components (<UserProfile>, <MyButton>). Lowercase = HTML element. Uppercase = React component.

Using Components

function Header() { return ( <header> <h1>My Website</h1> <nav>Home | About | Contact</nav> </header> ); } function Footer() { return ( <footer> <p>Β© 2024 My Website</p> </footer> ); } function App() { return ( <div> <Header /> {/* Using Header component */} <main> <p>Main content here</p> </main> <Footer /> {/* Using Footer component */} </div> ); }

Component Organization in Real Projects

src/ β”œβ”€β”€ components/ β”‚ β”œβ”€β”€ common/ # Shared/reusable components β”‚ β”‚ β”œβ”€β”€ Button.jsx β”‚ β”‚ β”œβ”€β”€ Input.jsx β”‚ β”‚ β”œβ”€β”€ Modal.jsx β”‚ β”‚ └── Card.jsx β”‚ β”œβ”€β”€ layout/ # Layout components β”‚ β”‚ β”œβ”€β”€ Header.jsx β”‚ β”‚ β”œβ”€β”€ Footer.jsx β”‚ β”‚ β”œβ”€β”€ Sidebar.jsx β”‚ β”‚ └── Layout.jsx β”‚ └── features/ # Feature-specific components β”‚ β”œβ”€β”€ auth/ β”‚ β”‚ β”œβ”€β”€ LoginForm.jsx β”‚ β”‚ └── SignupForm.jsx β”‚ └── dashboard/ β”‚ β”œβ”€β”€ DashboardCard.jsx β”‚ └── DashboardChart.jsx β”œβ”€β”€ pages/ # Page-level components β”‚ β”œβ”€β”€ HomePage.jsx β”‚ β”œβ”€β”€ AboutPage.jsx β”‚ └── DashboardPage.jsx β”œβ”€β”€ hooks/ # Custom hooks (Module 02) β”œβ”€β”€ utils/ # Utility functions β”œβ”€β”€ services/ # API calls β”œβ”€β”€ App.jsx └── main.jsx

5. Props: Passing Data Around

What Are Props?

Props (short for "properties") are how you pass data from a parent component to a child component. They're like function arguments.

Real-World Analogy

Think of a component as a vending machine:

  • The props are the buttons you press (selections you make)
  • The output is what the machine displays/dispenses

The same machine can give you different things based on which buttons you press.

// Without props β€” this component always shows the same thing function Greeting() { return <h1>Hello, World!</h1>; } // With props β€” now it's reusable and dynamic! function Greeting(props) { return <h1>Hello, {props.name}!</h1>; } // Usage: <Greeting name="Alice" /> // Renders: Hello, Alice! <Greeting name="Bob" /> // Renders: Hello, Bob! <Greeting name="Charlie" /> // Renders: Hello, Charlie!

Destructuring Props (Standard Practice)

// Instead of using props.name, props.age, etc. // Destructure them directly: function UserCard({ name, age, email, isAdmin }) { return ( <div className="user-card"> <h2>{name}</h2> <p>Age: {age}</p> <p>Email: {email}</p> {isAdmin && <span className="badge">Admin</span>} </div> ); } // Usage: <UserCard name="Sarah" age={28} email="sarah@example.com" isAdmin={true} />

Types of Props

function PropsDemo({ // String name, // name="Alice" // Number age, // age={28} β€” numbers need curly braces! // Boolean isActive, // isActive={true} or just: isActive // Array hobbies, // hobbies={['reading', 'coding']} // Object address, // address={{ city: 'NYC', zip: '10001' }} // Function onClick, // onClick={() => console.log('clicked')} // Component / JSX (yes, you can pass components!) icon, // icon={<StarIcon />} // Children (special prop) children, // Content between <Component> and </Component> }) { return <div>{/* Use the props here */}</div>; }

Default Props

// Method 1: Default parameters (recommended) function Button({ text = 'Click me', color = 'blue', size = 'medium' }) { return ( <button style={{ backgroundColor: color }} className={`btn-${size}`} > {text} </button> ); } // Usage: <Button /> // Uses all defaults <Button text="Submit" /> // Only overrides text <Button color="red" size="large" /> // Overrides color and size // Method 2: defaultProps (legacy, but still seen in codebases) function Button({ text, color }) { return <button style={{ backgroundColor: color }}>{text}</button>; } Button.defaultProps = { text: 'Click me', color: 'blue', };

The Children Prop

The children prop is special β€” it's whatever you put between the opening and closing tags of a component:

function Card({ children, title }) { return ( <div className="card"> <h2 className="card-title">{title}</h2> <div className="card-body"> {children} {/* Whatever is between <Card> and </Card> */} </div> </div> ); } // Usage: <Card title="Welcome"> <p>This paragraph is the children prop!</p> <button>And this button too!</button> </Card> // children can be anything: text, elements, other components, even functions <Card title="User Info"> <UserProfile user={currentUser} /> <UserStats stats={userStats} /> </Card>

πŸ” Props Are Read-Only (Immutable)

// ❌ NEVER modify props β€” this will cause bugs function Greeting(props) { props.name = 'Hacked!'; // ❌ DON'T DO THIS return <h1>Hello, {props.name}</h1>; } // βœ… Props are read-only. If you need to change data, use STATE (next section)

Real-World Analogy: Props are like a printed photograph someone hands you. You can look at it, but you can't change the actual photo. If you want a different photo, you need to ask for a new one.

Props Flow: One-Way Data Flow

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ React's One-Way Data Flow (Top-Down) β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ App β”‚ ← Has data (state) β”‚ β”‚ β”‚ name= β”‚ β”‚ β”‚ β”‚ "Alice" β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ passes name as prop β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”‚ β”‚ β”‚ Header β”‚ ← Receives name β”‚ β”‚ β”‚ {name} β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ passes name as prop β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”‚ β”‚ β”‚Greeting β”‚ ← Receives name β”‚ β”‚ β”‚ {name} β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ Data flows DOWN. Never UP. Never SIDEWAYS. β”‚ β”‚ (To send data up, use callback functions β€” β”‚ β”‚ we'll cover this soon) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Passing Functions as Props (Child-to-Parent Communication)

function ParentComponent() { const handleChildClick = (message) => { console.log('Message from child:', message); }; return ( <ChildComponent onButtonClick={handleChildClick} /> ); } function ChildComponent({ onButtonClick }) { return ( <button onClick={() => onButtonClick('Hello from child!')}> Click me </button> ); } // When the button is clicked: // 1. ChildComponent calls onButtonClick('Hello from child!') // 2. This actually calls ParentComponent's handleChildClick // 3. Console logs: "Message from child: Hello from child!"

Prop Drilling Problem (Preview for Module 04)

// When you need to pass props through many levels, it gets messy: function App() { const user = { name: 'Alice', role: 'admin' }; return <Layout user={user} />; // Pass to Layout } function Layout({ user }) { return <Sidebar user={user} />; // Pass to Sidebar } function Sidebar({ user }) { return <UserInfo user={user} />; // Pass to UserInfo } function UserInfo({ user }) { return <span>{user.name}</span>; // Finally use it! } // Layout and Sidebar don't even USE the user prop! // They're just passing it through. This is "prop drilling." // πŸ”— We'll solve this with Context API in Module 04.

6. State: Making Things Interactive

What is State?

State is data that changes over time and affects what the UI displays. When state changes, React re-renders the component to reflect the new state.

Props vs State

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Props vs State β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Props β”‚ State β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Passed from β”‚ Created inside the β”‚ β”‚ parent β”‚ component itself β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Read-only β”‚ Mutable (changeable) β”‚ β”‚ (immutable) β”‚ via setState β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Like function β”‚ Like local variables β”‚ β”‚ arguments β”‚ (but persistent) β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Component has β”‚ Component has FULL β”‚ β”‚ NO control β”‚ control β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Doesn't cause β”‚ Changes cause β”‚ β”‚ re-renders* β”‚ re-renders β”‚ β”‚ (*of itself) β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

useState Hook β€” The Basics

import { useState } from 'react'; function Counter() { // useState returns an array with exactly 2 elements: // [currentValue, setterFunction] const [count, setCount] = useState(0); // ^ ^ ^ // | | | // current function to initial // value update it value return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }

πŸ” Under the Hood: How useState Works

First render: 1. React calls Counter() 2. useState(0) β†’ React stores 0 internally, returns [0, setCount] 3. count = 0 4. JSX renders: "You clicked 0 times" User clicks button: 1. setCount(0 + 1) is called β†’ setCount(1) 2. React schedules a re-render 3. React calls Counter() AGAIN 4. useState(0) β†’ React IGNORES 0, returns stored value: [1, setCount] 5. count = 1 6. JSX renders: "You clicked 1 times" Note: The component function runs AGAIN from top to bottom! But useState remembers the state between renders.
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Where does React store state? β”‚ β”‚ β”‚ β”‚ React maintains an internal "fiber" tree. β”‚ β”‚ Each component instance has a fiber node β”‚ β”‚ that stores: β”‚ β”‚ - The state values (in a linked list) β”‚ β”‚ - The pending state updates (queue) β”‚ β”‚ - The hook order (important!) β”‚ β”‚ β”‚ β”‚ Component Instance β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Fiber Node β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ memoizedState ─────┐ β”‚ β”‚ β”‚ β”‚ β”‚ (linked list) β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ hook1 β†’ hook2 β†’ ...β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ └── updateQueue β”‚ β”‚ β”‚ β”‚ β”‚ (pending updates) β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ This is why hooks must be called in the β”‚ β”‚ same order every render! (Rules of Hooks) β”‚ β”‚ πŸ”— Deep dive in Module 02 β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Multiple State Variables

function UserForm() { // Each useState call manages independent state const [name, setName] = useState(''); const [email, setEmail] = useState(''); const [age, setAge] = useState(0); const [isSubscribed, setIsSubscribed] = useState(false); return ( <form> <input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name" /> <input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" /> <input type="number" value={age} onChange={(e) => setAge(Number(e.target.value))} placeholder="Age" /> <label> <input type="checkbox" checked={isSubscribed} onChange={(e) => setIsSubscribed(e.target.checked)} /> Subscribe to newsletter </label> </form> ); }

State with Objects and Arrays

function UserProfile() { const [user, setUser] = useState({ name: 'Alice', age: 28, address: { city: 'New York', zip: '10001' } }); // ❌ WRONG β€” Mutating state directly const updateCityWrong = () => { user.address.city = 'Los Angeles'; // Direct mutation! setUser(user); // React won't re-render because it's the same reference }; // βœ… CORRECT β€” Create a new object with the update const updateCity = () => { setUser({ ...user, // Copy all existing properties address: { ...user.address, // Copy existing address properties city: 'Los Angeles' // Override the city } }); }; // Working with arrays const [todos, setTodos] = useState([ { id: 1, text: 'Learn React', done: false }, { id: 2, text: 'Build a project', done: false }, ]); // βœ… Adding an item const addTodo = (text) => { setTodos([...todos, { id: Date.now(), text, done: false }]); }; // βœ… Removing an item const removeTodo = (id) => { setTodos(todos.filter(todo => todo.id !== id)); }; // βœ… Updating an item const toggleTodo = (id) => { setTodos(todos.map(todo => todo.id === id ? { ...todo, done: !todo.done } : todo )); }; return ( <div> <p>{user.name} lives in {user.address.city}</p> <button onClick={updateCity}>Move to LA</button> </div> ); }

⚠️ Critical Rule: NEVER mutate state directly. Always create a new copy. React compares object references (with ===) to decide if it should re-render. If you mutate the same object, the reference doesn't change, so React skips the re-render.

Functional Updates (When New State Depends on Old State)

function Counter() { const [count, setCount] = useState(0); // ❌ Potentially buggy when called multiple times rapidly const incrementThree = () => { setCount(count + 1); // All three use the same stale `count` setCount(count + 1); // Same stale `count` setCount(count + 1); // Same stale `count` // Result: count only goes up by 1, not 3! }; // βœ… Correct β€” use functional update form const incrementThreeCorrect = () => { setCount(prev => prev + 1); // prev = current count setCount(prev => prev + 1); // prev = result of previous setCount setCount(prev => prev + 1); // prev = result of previous setCount // Result: count goes up by 3! }; return ( <div> <p>Count: {count}</p> <button onClick={incrementThreeCorrect}>+3</button> </div> ); }

πŸ” Why does this happen? setCount(count + 1) captures the value of count at the time the function was created (closure). All three calls see the same count. With setCount(prev => prev + 1), React feeds each update the latest pending value.

🎯 Lifting State Up

When two components need to share state, move the state to their closest common parent:

// ❌ Problem: Two components need the same data but manage it separately function TemperatureInput() { const [temp, setTemp] = useState(''); // Its own state return <input value={temp} onChange={e => setTemp(e.target.value)} />; } // βœ… Solution: "Lift state up" to the parent function TemperatureCalculator() { // Parent owns the state const [celsius, setCelsius] = useState(''); const fahrenheit = celsius ? (parseFloat(celsius) * 9/5 + 32).toFixed(1) : ''; return ( <div> <h2>Temperature Calculator</h2> <label> Celsius: <input value={celsius} onChange={e => setCelsius(e.target.value)} /> </label> <p>In Fahrenheit: {fahrenheit}Β°F</p> </div> ); }

7. Event Handling in React

React Events vs DOM Events

// Vanilla DOM events: // <button onclick="handleClick()">Click</button> // React events: // <button onClick={handleClick}>Click</button> // Key differences: // 1. camelCase (onClick, not onclick) // 2. Pass a function REFERENCE, not a string // 3. React uses SyntheticEvents (wrapper around native events)

πŸ” Under the Hood: Synthetic Events

React doesn't attach event listeners to each individual DOM element. Instead:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ How React Handles Events β”‚ β”‚ β”‚ β”‚ Vanilla JS: β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ <button onclick=handler1> β”‚ ← Listener here β”‚ β”‚ β”‚ <button onclick=handler2> β”‚ ← Listener here β”‚ β”‚ β”‚ <button onclick=handler3> β”‚ ← Listener here β”‚ β”‚ β”‚ ... 1000 buttons β”‚ ← 1000 listeners β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ React (Event Delegation): β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ <div id="root"> β”‚ ← ONE listener β”‚ β”‚ β”‚ <button> (no listener) β”‚ (on root) β”‚ β”‚ β”‚ <button> (no listener) β”‚ β”‚ β”‚ β”‚ <button> (no listener) β”‚ β”‚ β”‚ β”‚ </div> β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ React attaches a single listener on the root. β”‚ β”‚ When an event fires, React: β”‚ β”‚ 1. Catches it at the root (via bubbling) β”‚ β”‚ 2. Wraps the native event in a SyntheticEvent β”‚ β”‚ 3. Finds the target component β”‚ β”‚ 4. Calls the correct handler β”‚ β”‚ β”‚ β”‚ Benefits: Better performance, consistent β”‚ β”‚ cross-browser behavior, automatic cleanup. β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Common Event Patterns

function EventExamples() { // Pattern 1: Inline arrow function return <button onClick={() => console.log('clicked')}>Click</button>; // Pattern 2: Named function const handleClick = () => { console.log('clicked'); }; return <button onClick={handleClick}>Click</button>; // Pattern 3: With event object const handleInputChange = (event) => { console.log('New value:', event.target.value); }; return <input onChange={handleInputChange} />; // Pattern 4: Passing arguments to handlers const handleDelete = (id) => { console.log('Delete item:', id); }; return <button onClick={() => handleDelete(42)}>Delete</button>; // ❌ COMMON MISTAKE: Calling the function instead of passing it // This will fire immediately on render, not on click! return <button onClick={handleDelete(42)}>Delete</button>; // Fix: Wrap in arrow function: onClick={() => handleDelete(42)} }

Preventing Default Behavior

function LoginForm() { const handleSubmit = (event) => { event.preventDefault(); // Prevents the page from reloading // Now handle the form submission with JavaScript console.log('Form submitted!'); }; return ( <form onSubmit={handleSubmit}> <input type="email" placeholder="Email" /> <input type="password" placeholder="Password" /> <button type="submit">Log In</button> </form> ); }

Common React Events

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Event Type β”‚ React Handler β”‚ When? β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Click β”‚ onClick β”‚ Element β”‚ β”‚ β”‚ β”‚ clicked β”‚ β”‚ Double Click β”‚ onDoubleClick β”‚ Double β”‚ β”‚ β”‚ β”‚ clicked β”‚ β”‚ Change β”‚ onChange β”‚ Input β”‚ β”‚ β”‚ β”‚ value β”‚ β”‚ β”‚ β”‚ changes β”‚ β”‚ Submit β”‚ onSubmit β”‚ Form β”‚ β”‚ β”‚ β”‚ submitted β”‚ β”‚ Focus β”‚ onFocus β”‚ Element β”‚ β”‚ β”‚ β”‚ gets focus β”‚ β”‚ Blur β”‚ onBlur β”‚ Element β”‚ β”‚ β”‚ β”‚ loses β”‚ β”‚ β”‚ β”‚ focus β”‚ β”‚ Key Down β”‚ onKeyDown β”‚ Key is β”‚ β”‚ β”‚ β”‚ pressed β”‚ β”‚ Key Up β”‚ onKeyUp β”‚ Key is β”‚ β”‚ β”‚ β”‚ released β”‚ β”‚ Mouse Enter β”‚ onMouseEnter β”‚ Mouse β”‚ β”‚ β”‚ β”‚ enters β”‚ β”‚ Mouse Leave β”‚ onMouseLeave β”‚ Mouse β”‚ β”‚ β”‚ β”‚ leaves β”‚ β”‚ Scroll β”‚ onScroll β”‚ Element β”‚ β”‚ β”‚ β”‚ scrolls β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

8. Conditional Rendering

React lets you render different UI based on conditions. There are several patterns:

Pattern 1: If/Else (Outside JSX)

function Greeting({ isLoggedIn }) { if (isLoggedIn) { return <h1>Welcome back!</h1>; } return <h1>Please sign in.</h1>; }

Pattern 2: Ternary Operator (Inside JSX)

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

Pattern 3: Logical AND (&&) β€” Render or Nothing

function Notifications({ count }) { return ( <div> <h1>Dashboard</h1> {count > 0 && ( <span className="badge">{count} new notifications</span> )} {/* If count is 0 or negative, nothing renders */} </div> ); }

⚠️ Gotcha with &&: Beware of 0 as a falsy value!

// ❌ Bug: When count is 0, it renders "0" on screen {count && <span>{count} items</span>} // When count = 0: renders "0" (because 0 is falsy but React renders the number) // βœ… Fix: Explicitly check the condition {count > 0 && <span>{count} items</span>} // When count = 0: renders nothing

Pattern 4: Switch or Object Mapping (Multiple Conditions)

function StatusBadge({ status }) { const badges = { active: <span className="badge-green">Active</span>, inactive: <span className="badge-gray">Inactive</span>, pending: <span className="badge-yellow">Pending</span>, banned: <span className="badge-red">Banned</span>, }; return badges[status] || <span className="badge-gray">Unknown</span>; } // Usage: <StatusBadge status="active" /> // Green badge <StatusBadge status="banned" /> // Red badge <StatusBadge status="xyz" /> // Gray "Unknown" badge

Pattern 5: Early Return

function UserDashboard({ user, isLoading, error }) { // Handle edge cases first, return early if (isLoading) { return <LoadingSpinner />; } if (error) { return <ErrorMessage message={error.message} />; } if (!user) { return <p>No user found.</p>; } // Main render β€” only reached if all checks pass return ( <div className="dashboard"> <h1>Welcome, {user.name}</h1> {/* ... rest of dashboard */} </div> ); }

9. Rendering Lists and the Key Prop

Rendering Lists with .map()

function TodoList() { const todos = [ { id: 1, text: 'Learn React', done: true }, { id: 2, text: 'Build a project', done: false }, { id: 3, text: 'Get hired', done: false }, ]; return ( <ul> {todos.map(todo => ( <li key={todo.id}> {todo.done ? 'βœ…' : '⬜'} {todo.text} </li> ))} </ul> ); }

🎯 The Key Prop β€” Why It Matters (Interview Favorite!)

// Every element in a list MUST have a unique "key" prop {items.map(item => ( <ListItem key={item.id} data={item} /> ))}

πŸ” Under the Hood: Why Keys Exist

When a list changes, React needs to figure out what changed. Keys help React identify which items are added, removed, or reordered.

Without keys (React uses index by default): β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Before: After (insert at beginning): β”‚ β”‚ [0] Alice [0] Zara ← React thinks Alice β”‚ β”‚ [1] Bob [1] Alice changed to Zara β”‚ β”‚ [2] Charlie [2] Bob ← Thinks Bob changed β”‚ β”‚ [3] Charlie to Alice (!) β”‚ β”‚ β”‚ β”‚ React updates ALL items because the indices β”‚ β”‚ shifted! Very inefficient. β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ With keys (unique identifiers): β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Before: After (insert at beginning): β”‚ β”‚ key="a" Alice key="z" Zara ← New! Insert β”‚ β”‚ key="b" Bob key="a" Alice ← Same, keep β”‚ β”‚ key="c" Charlie key="b" Bob ← Same, keep β”‚ β”‚ key="c" Charlie ← Same, keep β”‚ β”‚ β”‚ β”‚ React knows only Zara is new. It inserts ONE β”‚ β”‚ element. Existing elements are untouched! β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

⚠️ Why Array Index as Key is Bad

// ❌ Using index as key β€” BAD for dynamic lists {todos.map((todo, index) => ( <TodoItem key={index} data={todo} /> ))} // This causes bugs when: // 1. Items are reordered (keys stay the same, items change) // 2. Items are inserted/deleted at the beginning or middle // 3. Components have internal state (state gets mapped to wrong item) // βœ… Use a unique identifier {todos.map(todo => ( <TodoItem key={todo.id} data={todo} /> ))}

When Index as Key is OK

// Index as key is acceptable ONLY when: // 1. The list is static (never changes) // 2. Items are never reordered // 3. Items are never inserted or deleted from the middle // 4. Items don't have internal state // Example: A static navigation menu const navItems = ['Home', 'About', 'Contact']; {navItems.map((item, index) => ( <li key={index}>{item}</li> // OK β€” this list never changes ))}

Key Rules

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Key Rules: β”‚ β”‚ β”‚ β”‚ 1. Keys must be UNIQUE among siblings β”‚ β”‚ (not globally unique, just within the list) β”‚ β”‚ β”‚ β”‚ 2. Keys must be STABLE (don't change between β”‚ β”‚ renders) β€” never use Math.random()! β”‚ β”‚ β”‚ β”‚ 3. Keys should be from your DATA (database ID, β”‚ β”‚ UUID, etc.) β”‚ β”‚ β”‚ β”‚ 4. Keys are NOT passed as a prop to the β”‚ β”‚ component β€” they're React-internal only β”‚ β”‚ β”‚ β”‚ 5. Don't generate keys during render: β”‚ β”‚ key={crypto.randomUUID()} ← BAD! β”‚ β”‚ This creates a new key every render, β”‚ β”‚ forcing React to unmount and remount. β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

10. Forms and Controlled Components

Controlled Components

In a controlled component, React controls the form element's value through state:

import { useState } from 'react'; function ContactForm() { const [formData, setFormData] = useState({ name: '', email: '', message: '', category: 'general', }); const handleChange = (e) => { const { name, value } = e.target; setFormData(prev => ({ ...prev, [name]: value, // Computed property name β€” uses the input's "name" attribute })); }; const handleSubmit = (e) => { e.preventDefault(); console.log('Form data:', formData); // Send to API, validate, etc. }; return ( <form onSubmit={handleSubmit}> <input type="text" name="name" value={formData.name} // React controls the value onChange={handleChange} // React updates the value placeholder="Your name" /> <input type="email" name="email" value={formData.email} onChange={handleChange} placeholder="Your email" /> <select name="category" value={formData.category} onChange={handleChange} > <option value="general">General</option> <option value="bug">Bug Report</option> <option value="feature">Feature Request</option> </select> <textarea name="message" value={formData.message} onChange={handleChange} placeholder="Your message" /> <button type="submit">Send</button> </form> ); }

Uncontrolled Components

In an uncontrolled component, the DOM manages the form element's value. You read it when needed using a ref:

import { useRef } from 'react'; function UncontrolledForm() { const nameRef = useRef(null); const emailRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); // Read values directly from DOM elements console.log('Name:', nameRef.current.value); console.log('Email:', emailRef.current.value); }; return ( <form onSubmit={handleSubmit}> <input type="text" ref={nameRef} defaultValue="" /> <input type="email" ref={emailRef} defaultValue="" /> <button type="submit">Submit</button> </form> ); }

πŸ“Š Controlled vs Uncontrolled Components

FeatureControlledUncontrolled
Value stored inReact stateDOM
Update mechanismonChange + setStateDOM handles it
Access valueFrom state variableFrom ref.current.value
ValidationOn every keystrokeOn submit
Instant feedbackβœ… Easy❌ Hard
Form resetSet state to initialReset form / clear refs
PerformanceMore re-rendersFewer re-renders
Default value propvaluedefaultValue
When to useMost casesFile inputs, third-party DOM libs

🎯 Interview Answer: "I prefer controlled components because they give React full control over the form data, making it easier to validate, transform, and sync with other UI. I use uncontrolled components mainly for file inputs or when integrating with non-React libraries."


11. Component Composition vs Inheritance

React Strongly Favors Composition Over Inheritance

// ❌ Don't use inheritance for component reuse class SpecialButton extends Button { // Don't do this! render() { return <button className="special">{this.props.children}</button>; } } // βœ… Use composition β€” pass components as props or children function Dialog({ title, children, footer }) { return ( <div className="dialog-overlay"> <div className="dialog"> <h2 className="dialog-title">{title}</h2> <div className="dialog-body">{children}</div> {footer && <div className="dialog-footer">{footer}</div>} </div> </div> ); } // Specialized dialog via composition, not inheritance function ConfirmDialog({ onConfirm, onCancel, message }) { return ( <Dialog title="Confirm Action" footer={ <div> <button onClick={onCancel}>Cancel</button> <button onClick={onConfirm}>Confirm</button> </div> } > <p>{message}</p> </Dialog> ); } // Another specialized dialog function AlertDialog({ onClose, message }) { return ( <Dialog title="Alert" footer={<button onClick={onClose}>OK</button>} > <p className="alert-text">{message}</p> </Dialog> ); }

Specialization Pattern

// Generic Button function Button({ variant = 'primary', size = 'medium', children, ...rest }) { return ( <button className={`btn btn-${variant} btn-${size}`} {...rest} > {children} </button> ); } // Specialized Buttons (via composition, not inheritance!) function DangerButton(props) { return <Button variant="danger" {...props} />; } function IconButton({ icon, children, ...props }) { return ( <Button {...props}> <span className="icon">{icon}</span> {children} </Button> ); } // Usage: <Button>Normal Button</Button> <DangerButton onClick={handleDelete}>Delete</DangerButton> <IconButton icon="πŸ”">Search</IconButton>

12. The Virtual DOM β€” How React Actually Works

πŸ” What Is the Virtual DOM?

The Virtual DOM is a lightweight JavaScript object tree that mirrors the structure of the real DOM. It's React's internal representation of what the UI should look like.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Your JSX β†’ Virtual DOM β†’ Real DOM β”‚ β”‚ β”‚ β”‚ <div> { β”‚ β”‚ <h1>Hello</h1> type: 'div', β”‚ β”‚ <p>World</p> children: [ β”‚ β”‚ </div> { β”‚ β”‚ type: 'h1', β”‚ β”‚ children: 'Hello' β”‚ β”‚ }, β”‚ β”‚ { β”‚ β”‚ type: 'p', β”‚ β”‚ children: 'World' β”‚ β”‚ } β”‚ β”‚ ] β”‚ β”‚ } β”‚ β”‚ β”‚ β”‚ The Virtual DOM is just a JavaScript object! β”‚ β”‚ It's cheap to create and compare. β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Reconciliation Process

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Step-by-step: What Happens When State Changes β”‚ β”‚ β”‚ β”‚ 1. State change triggers a re-render β”‚ β”‚ setCount(count + 1) β”‚ β”‚ β”‚ β”‚ 2. React calls your component function again β”‚ β”‚ Counter() β†’ returns new JSX β”‚ β”‚ β”‚ β”‚ 3. React creates a new Virtual DOM tree β”‚ β”‚ New tree based on new JSX return value β”‚ β”‚ β”‚ β”‚ 4. React DIFFS the old and new Virtual DOM trees β”‚ β”‚ "What changed between the old tree and new tree?" β”‚ β”‚ β”‚ β”‚ Old: <p>Count: 5</p> β”‚ β”‚ New: <p>Count: 6</p> β”‚ β”‚ Diff: Text content changed from "5" to "6" β”‚ β”‚ β”‚ β”‚ 5. React computes the minimal set of DOM operations β”‚ β”‚ "Only update the text node, nothing else" β”‚ β”‚ β”‚ β”‚ 6. React applies ONLY those changes to the real DOM β”‚ β”‚ document.querySelector('p').textContent = '6' β”‚ β”‚ (React doesn't actually use querySelector β€” β”‚ β”‚ it tracks DOM references internally) β”‚ β”‚ β”‚ β”‚ The key insight: React touches the real DOM as β”‚ β”‚ LITTLE as possible because DOM operations are slow. β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Why Not Just Update the Real DOM Directly?

Performance comparison (simplified): Real DOM operations: createElement β†’ ~10ΞΌs ┐ setAttribute β†’ ~5ΞΌs β”‚ Touching the real DOM appendChild β†’ ~10ΞΌs β”‚ triggers layout innerHTML change β†’ ~50ΞΌs β”‚ recalculation, repainting, style change β†’ ~5ΞΌs β”‚ reflowing β€” EXPENSIVE! reflow β†’ ~ms β”˜ Virtual DOM operations: Create JS object β†’ ~0.1ΞΌs ┐ Compare objects β†’ ~0.1ΞΌs β”‚ Just JavaScript objects Update property β†’ ~0.01ΞΌs β”‚ in memory β€” CHEAP! β”˜ By batching changes and computing the minimal DOM update, React avoids unnecessary expensive real DOM operations.

Diffing Algorithm Rules

React's diffing algorithm uses heuristics to achieve O(n) complexity (instead of O(nΒ³)):

Rule 1: Different element types β†’ destroy and rebuild Old: <div><Counter /></div> New: <span><Counter /></span> React destroys the <div> and everything inside it, then creates a new <span> from scratch. Counter's state is LOST! Rule 2: Same element type β†’ update attributes only Old: <div className="old" title="x" /> New: <div className="new" title="x" /> React only updates className. title stays the same. Rule 3: Keys for list items Covered in Section 9. Keys let React match items across renders without relying on order.

πŸ”— Deep Dive: We'll explore the Fiber architecture and concurrent rendering in Module 05, which fundamentally changed how React performs these updates.


13. Common Mistakes & Gotchas

Mistake 1: Forgetting that setState is Asynchronous

function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); console.log(count); // ⚠️ Still logs OLD value (0)! // setCount doesn't update immediately // The new value is available on the NEXT render }; // If you need the new value immediately, compute it first: const handleClickCorrect = () => { const newCount = count + 1; setCount(newCount); console.log(newCount); // βœ… Logs 1 }; }

Mistake 2: Mutating State Directly

// ❌ BAD const [items, setItems] = useState(['a', 'b', 'c']); const addItem = () => { items.push('d'); // Mutating the existing array! setItems(items); // React sees same reference, skips re-render }; // βœ… GOOD const addItem = () => { setItems([...items, 'd']); // New array = new reference = re-render };

Mistake 3: Calling Function Instead of Passing Reference

// ❌ Executes immediately on every render! <button onClick={handleDelete(id)}>Delete</button> // βœ… Passes a function reference <button onClick={() => handleDelete(id)}>Delete</button> // βœ… Also works (if no arguments needed) <button onClick={handleDelete}>Delete</button>

Mistake 4: Missing Key in Lists

// ❌ Warning: Each child in a list should have a unique "key" prop {items.map(item => <li>{item.name}</li>)} // βœ… {items.map(item => <li key={item.id}>{item.name}</li>)}

Mistake 5: Using && with Numbers

// ❌ When count = 0, this renders "0" on screen {count && <span>{count} items</span>} // βœ… Fixed β€” explicit boolean check {count > 0 && <span>{count} items</span>}

Mistake 6: Infinite Loops with useEffect

// ❌ Infinite loop! setCount causes re-render, useEffect runs again useEffect(() => { setCount(count + 1); }); // No dependency array = runs on EVERY render // βœ… Run only once on mount useEffect(() => { setCount(count + 1); }, []); // Empty dependency array = run once

πŸ”— We'll cover useEffect in depth in Module 02.

Mistake 7: Conditional Hook Calls

// ❌ NEVER call hooks conditionally function Component({ isAdmin }) { if (isAdmin) { const [name, setName] = useState(''); // ❌ Hook inside if! } // React relies on hooks being called in the SAME ORDER every render // Conditional hooks break this assumption // βœ… Always call hooks at the top level const [name, setName] = useState(''); // Then use the condition elsewhere if (isAdmin) { // use name here } }

14. Practice Problems

πŸ§ͺ Problem 1: Traffic Light

Build a TrafficLight component that cycles through red β†’ yellow β†’ green on button click.

Requirements:

  • Display a colored circle (red, yellow, or green)
  • A "Next" button to cycle to the next color
  • Display the current color name
<details> <summary>πŸ’‘ Hint</summary>

Use useState to track the current color index. Use an array of colors and modulo operator to cycle.

</details> <details> <summary>βœ… Solution</summary>
import { useState } from 'react'; function TrafficLight() { const colors = ['red', 'yellow', 'green']; const [currentIndex, setCurrentIndex] = useState(0); const nextLight = () => { setCurrentIndex((prev) => (prev + 1) % colors.length); }; const currentColor = colors[currentIndex]; return ( <div style={{ textAlign: 'center', padding: '20px' }}> <div style={{ width: '100px', height: '100px', borderRadius: '50%', backgroundColor: currentColor, margin: '20px auto', border: '3px solid #333', boxShadow: `0 0 20px ${currentColor}`, transition: 'background-color 0.3s ease', }} /> <p style={{ fontSize: '24px', textTransform: 'capitalize' }}> {currentColor} </p> <button onClick={nextLight} style={{ padding: '10px 20px', fontSize: '16px', cursor: 'pointer', backgroundColor: '#333', color: 'white', border: 'none', borderRadius: '5px', }} > Next Light </button> </div> ); } export default TrafficLight;
</details>

πŸ§ͺ Problem 2: Character Counter

Build a CharacterCounter component β€” a textarea with a live character count and max character limit.

Requirements:

  • Textarea input
  • Show current character count / max (e.g., "45 / 280")
  • Change count color to red when within 20 characters of the limit
  • Disable further typing when limit is reached
<details> <summary>βœ… Solution</summary>
import { useState } from 'react'; function CharacterCounter({ maxLength = 280 }) { const [text, setText] = useState(''); const remaining = maxLength - text.length; const isWarning = remaining <= 20 && remaining > 0; const isOver = remaining <= 0; const handleChange = (e) => { if (e.target.value.length <= maxLength) { setText(e.target.value); } }; return ( <div style={{ maxWidth: '400px', margin: '20px auto' }}> <textarea value={text} onChange={handleChange} placeholder="What's on your mind?" rows={5} style={{ width: '100%', padding: '12px', fontSize: '16px', borderRadius: '8px', border: `2px solid ${isOver ? 'red' : isWarning ? 'orange' : '#ccc'}`, resize: 'vertical', fontFamily: 'inherit', }} /> <div style={{ textAlign: 'right', marginTop: '4px', fontSize: '14px', color: isOver ? 'red' : isWarning ? 'orange' : '#666', fontWeight: isWarning || isOver ? 'bold' : 'normal', }}> {text.length} / {maxLength} </div> </div> ); } export default CharacterCounter;
</details>

πŸ§ͺ Problem 3: Filterable Product List

Build a product list with search filtering.

Requirements:

  • An array of products with name, category, and price
  • A search input that filters products by name (case-insensitive)
  • Display matching products in a table
  • Show "No results found" when nothing matches
<details> <summary>βœ… Solution</summary>
import { useState } from 'react'; const PRODUCTS = [ { id: 1, name: 'MacBook Pro', category: 'Electronics', price: 2499 }, { id: 2, name: 'iPhone 15', category: 'Electronics', price: 999 }, { id: 3, name: 'Running Shoes', category: 'Sports', price: 129 }, { id: 4, name: 'Coffee Maker', category: 'Kitchen', price: 79 }, { id: 5, name: 'Yoga Mat', category: 'Sports', price: 35 }, { id: 6, name: 'Headphones', category: 'Electronics', price: 349 }, { id: 7, name: 'Blender', category: 'Kitchen', price: 45 }, { id: 8, name: 'Backpack', category: 'Accessories', price: 89 }, ]; function ProductList() { const [searchTerm, setSearchTerm] = useState(''); const filteredProducts = PRODUCTS.filter(product => product.name.toLowerCase().includes(searchTerm.toLowerCase()) ); return ( <div style={{ maxWidth: '600px', margin: '20px auto', fontFamily: 'Arial' }}> <h1>Product Catalog</h1> <input type="text" value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} placeholder="πŸ” Search products..." style={{ width: '100%', padding: '12px', fontSize: '16px', borderRadius: '8px', border: '2px solid #ddd', marginBottom: '16px', boxSizing: 'border-box', }} /> {filteredProducts.length === 0 ? ( <p style={{ textAlign: 'center', color: '#999', fontSize: '18px' }}> No products found matching "{searchTerm}" </p> ) : ( <table style={{ width: '100%', borderCollapse: 'collapse' }}> <thead> <tr style={{ backgroundColor: '#f5f5f5' }}> <th style={{ padding: '12px', textAlign: 'left', borderBottom: '2px solid #ddd' }}> Name </th> <th style={{ padding: '12px', textAlign: 'left', borderBottom: '2px solid #ddd' }}> Category </th> <th style={{ padding: '12px', textAlign: 'right', borderBottom: '2px solid #ddd' }}> Price </th> </tr> </thead> <tbody> {filteredProducts.map(product => ( <tr key={product.id} style={{ borderBottom: '1px solid #eee' }}> <td style={{ padding: '12px' }}>{product.name}</td> <td style={{ padding: '12px' }}> <span style={{ padding: '2px 8px', borderRadius: '12px', backgroundColor: '#e8f4fd', fontSize: '13px', }}> {product.category} </span> </td> <td style={{ padding: '12px', textAlign: 'right', fontWeight: 'bold' }}> ${product.price} </td> </tr> ))} </tbody> </table> )} <p style={{ color: '#999', fontSize: '14px', marginTop: '8px' }}> Showing {filteredProducts.length} of {PRODUCTS.length} products </p> </div> ); } export default ProductList;
</details>

πŸ§ͺ Problem 4: Accordion Component

Build an Accordion component where only one section can be open at a time.

<details> <summary>βœ… Solution</summary>
import { useState } from 'react'; const FAQ_DATA = [ { id: 1, question: 'What is React?', answer: 'React is a JavaScript library for building user interfaces. It was created by Facebook and lets you build UIs from individual pieces called components.', }, { id: 2, question: 'What is JSX?', answer: 'JSX is a syntax extension for JavaScript that lets you write HTML-like markup inside a JavaScript file. It gets compiled to React.createElement() calls.', }, { id: 3, question: 'What are hooks?', answer: 'Hooks are functions that let you use state and other React features in function components. The most common hooks are useState and useEffect.', }, { id: 4, question: 'What is the Virtual DOM?', answer: 'The Virtual DOM is a lightweight JavaScript representation of the real DOM. React uses it to efficiently calculate what changes need to be made to the actual DOM.', }, ]; function Accordion() { const [openId, setOpenId] = useState(null); const toggleItem = (id) => { setOpenId(prevId => prevId === id ? null : id); }; return ( <div style={{ maxWidth: '600px', margin: '20px auto', fontFamily: 'Arial' }}> <h1>Frequently Asked Questions</h1> {FAQ_DATA.map(item => ( <div key={item.id} style={{ border: '1px solid #ddd', borderRadius: '8px', marginBottom: '8px', overflow: 'hidden', }} > <button onClick={() => toggleItem(item.id)} style={{ width: '100%', padding: '16px', fontSize: '16px', fontWeight: 'bold', textAlign: 'left', backgroundColor: openId === item.id ? '#f0f4ff' : '#fff', border: 'none', cursor: 'pointer', display: 'flex', justifyContent: 'space-between', alignItems: 'center', }} > {item.question} <span style={{ transform: openId === item.id ? 'rotate(180deg)' : 'rotate(0)', transition: 'transform 0.2s ease', fontSize: '20px', }}> β–Ό </span> </button> {openId === item.id && ( <div style={{ padding: '16px', backgroundColor: '#f9f9f9', borderTop: '1px solid #eee', lineHeight: '1.6', }}> {item.answer} </div> )} </div> ))} </div> ); } export default Accordion;
</details>

πŸ§ͺ Problem 5: Star Rating Component

Build a reusable StarRating component.

Requirements:

  • Display 5 stars
  • Click to set rating
  • Hover to preview rating
  • Show the current rating as text
<details> <summary>βœ… Solution</summary>
import { useState } from 'react'; function StarRating({ totalStars = 5, onChange }) { const [rating, setRating] = useState(0); const [hoverRating, setHoverRating] = useState(0); const handleClick = (starIndex) => { setRating(starIndex); if (onChange) onChange(starIndex); }; const labels = ['', 'Poor', 'Fair', 'Good', 'Very Good', 'Excellent']; return ( <div style={{ fontFamily: 'Arial', textAlign: 'center', padding: '20px' }}> <div style={{ fontSize: '40px', cursor: 'pointer', userSelect: 'none' }}> {Array.from({ length: totalStars }, (_, index) => { const starIndex = index + 1; const isFilled = starIndex <= (hoverRating || rating); return ( <span key={starIndex} onClick={() => handleClick(starIndex)} onMouseEnter={() => setHoverRating(starIndex)} onMouseLeave={() => setHoverRating(0)} style={{ color: isFilled ? '#ffc107' : '#e0e0e0', transition: 'color 0.1s ease, transform 0.1s ease', display: 'inline-block', transform: hoverRating === starIndex ? 'scale(1.2)' : 'scale(1)', }} > β˜… </span> ); })} </div> <p style={{ fontSize: '18px', color: '#555', marginTop: '8px' }}> {rating > 0 ? `${rating} / ${totalStars} β€” ${labels[rating]}` : 'Click to rate'} </p> </div> ); } export default StarRating;
</details>

15. Interview Questions

❓ Q1: What is React? Why would you use it?

Answer: React is a JavaScript library for building user interfaces, created by Meta (Facebook). Key reasons to use it:

  1. Declarative: You describe what the UI should look like, not how to update it
  2. Component-based: Build encapsulated, reusable UI pieces
  3. Virtual DOM: Efficient updates via diffing algorithm
  4. Large ecosystem: Rich library ecosystem and community
  5. Cross-platform: React Native for mobile, React for web
  6. One-way data flow: Predictable data flow makes debugging easier

It's a library, not a framework β€” it handles only the view layer. You choose your own router, state management, etc.


❓ Q2: What is JSX? Can React work without it?

Answer: JSX is a syntax extension that lets you write HTML-like code in JavaScript. It compiles to React.createElement() calls.

Yes, React works without JSX β€” you'd just call React.createElement directly:

// With JSX: const element = <h1 className="title">Hello</h1>; // Without JSX (equivalent): const element = React.createElement('h1', { className: 'title' }, 'Hello');

Nobody writes React without JSX in practice β€” it's just syntactic sugar, but it makes code far more readable.


❓ Q3: What is the Virtual DOM? How does it improve performance?

Answer: The Virtual DOM is a lightweight JavaScript object representation of the real DOM tree. When state changes:

  1. React creates a new Virtual DOM tree
  2. Diffs the new tree against the previous tree
  3. Computes the minimal set of changes needed
  4. Batches those changes and applies them to the real DOM in one go

This is faster than direct DOM manipulation because:

  • DOM operations are expensive (layout, paint, reflow)
  • JavaScript object operations are cheap
  • Batching reduces the number of DOM touches
  • The diffing algorithm is O(n) thanks to heuristics

Common follow-up: "Is the Virtual DOM always faster than direct DOM manipulation?"

No. For simple, targeted updates (changing one element's text), direct DOM manipulation can be faster. The Virtual DOM's advantage comes with complex UIs where many elements might need updating β€” it automates the process of figuring out what changed.


❓ Q4: What are the differences between state and props?

Answer:

AspectPropsState
SourceReceived from parentCreated within component
MutabilityImmutable (read-only)Mutable (via setState)
PurposeConfigure a componentInternal data that changes
Re-rendersWhen parent passes new propsWhen setState is called
OwnershipOwned by parentOwned by component
AnalogyFunction parametersLocal variables

Key insight: Props flow down (parent β†’ child). To send data up, parents pass callback functions as props that children can call.


❓ Q5: Why do we need keys in lists? What happens if we use array index?

Answer: Keys help React's reconciliation algorithm identify which items changed, were added, or were removed during re-renders.

Without keys (or with index keys), when an item is added to the beginning of a list, React thinks every item changed (because all indices shifted). This causes:

  • Unnecessary re-renders of every list item
  • State bugs: If items have internal state (e.g., input values), state gets assigned to the wrong items after reorder

With stable keys (database IDs), React correctly identifies:

  • Which items are new β†’ create them
  • Which items were removed β†’ destroy them
  • Which items moved β†’ reorder them
  • Which items are the same β†’ leave them alone

Use index only when: The list is static, never reordered, and items have no internal state.


❓ Q6: Explain the component lifecycle in functional components.

Answer: While functional components don't have lifecycle methods like class components, the concept still applies:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Mounting (Component appears on screen) β”‚ β”‚ 1. Component function is called β”‚ β”‚ 2. JSX is evaluated β”‚ β”‚ 3. DOM is updated β”‚ β”‚ 4. useEffect(() => {...}, []) runs β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Updating (State or props change) β”‚ β”‚ 1. Component function is called again β”‚ β”‚ 2. New JSX is evaluated β”‚ β”‚ 3. React diffs old vs new Virtual DOM β”‚ β”‚ 4. DOM is updated with minimal changes β”‚ β”‚ 5. useEffect cleanup runs (if deps changed)β”‚ β”‚ 6. useEffect callback runs β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ Unmounting (Component removed from screen) β”‚ β”‚ 1. useEffect cleanup function runs β”‚ β”‚ 2. Component is removed from DOM β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

❓ Q7: What is the difference between controlled and uncontrolled components?

Answer: A controlled component has its form value managed by React state:

const [value, setValue] = useState(''); <input value={value} onChange={e => setValue(e.target.value)} />

An uncontrolled component lets the DOM manage the value; you read it when needed:

const inputRef = useRef(); <input ref={inputRef} defaultValue="" /> // Access later: inputRef.current.value

When to use each:

  • Controlled: Most cases β€” gives you instant validation, transformation, and sync
  • Uncontrolled: File inputs (<input type="file">), integrating with third-party DOM libraries

❓ Q8: Why does React use className instead of class?

Answer: Because class is a reserved keyword in JavaScript (used for ES6 classes). Since JSX compiles to JavaScript function calls, using class would create a syntax error. Similarly, for becomes htmlFor because for is a JavaScript keyword (used in for loops).


❓ Q9: What is React.Fragment? When would you use it?

Answer: React.Fragment lets you group multiple elements without adding an extra DOM node:

// Without Fragment β€” adds an unnecessary <div> return ( <div> <h1>Title</h1> <p>Content</p> </div> ); // With Fragment β€” no extra DOM node return ( <> <h1>Title</h1> <p>Content</p> </> );

Use it when you need to return multiple elements from a component but don't want to add an extra wrapper element (which could break CSS layouts like Flexbox/Grid or create invalid HTML like <tr><div>...</div></tr>).


❓ Q10: What is "lifting state up"? When would you do it?

Answer: Lifting state up means moving state from a child component to the nearest common ancestor when multiple components need to share that state.

When: Two sibling components need to reflect the same changing data. Instead of duplicating state, move it to their parent and pass it down as props.

Example: A temperature converter with Celsius and Fahrenheit inputs β€” both need to stay in sync, so the temperature state lives in the parent.


16. "What's the Output?" Challenges

Challenge 1

function App() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); setCount(count + 1); setCount(count + 1); }; return <p>Count: {count}</p>; } // After one click, what is count?
<details> <summary>Answer</summary>

Count is 1, not 3.

All three setCount(count + 1) calls use the same stale count value (0). So all three calls are setCount(0 + 1), which is setCount(1) three times. React batches them, and the result is 1.

Fix: Use functional updates: setCount(prev => prev + 1) β€” then count would be 3.

</details>

Challenge 2

function App() { const [items, setItems] = useState([1, 2, 3]); const addItem = () => { items.push(4); setItems(items); }; return ( <div> {items.map((item, i) => <span key={i}>{item} </span>)} <button onClick={addItem}>Add</button> </div> ); } // What happens when you click "Add"?
<details> <summary>Answer</summary>

Nothing visible happens! (No re-render)

items.push(4) mutates the existing array. setItems(items) passes the same array reference. React does a === comparison and sees the reference hasn't changed, so it skips the re-render.

Fix: setItems([...items, 4]) β€” creates a new array reference.

</details>

Challenge 3

function App() { console.log('1. App renders'); const [count, setCount] = useState(() => { console.log('2. useState initializer'); return 0; }); console.log('3. After useState'); return ( <div> <p>{count}</p> <button onClick={() => setCount(c => c + 1)}>+</button> </div> ); } // What logs on first render? On clicking the button?
<details> <summary>Answer</summary>

First render:

1. App renders 2. useState initializer 3. After useState

On button click:

1. App renders 3. After useState

The useState initializer function only runs on the first render. On subsequent renders, React uses the stored state value and skips the initializer. This is why you'd use a function initializer for expensive computations β€” they only run once.

</details>

Challenge 4

function Parent() { console.log('Parent renders'); const [count, setCount] = useState(0); return ( <div> <button onClick={() => setCount(c => c + 1)}>Click</button> <Child /> </div> ); } function Child() { console.log('Child renders'); return <p>I am a child</p>; } // What logs when you click the button?
<details> <summary>Answer</summary>
Parent renders Child renders

Both Parent and Child re-render. When a parent re-renders, all its children re-render by default, even if their props haven't changed. This is a key concept for performance optimization.

To prevent unnecessary Child re-renders: Use React.memo(Child). We'll cover this in πŸ”— Module 05.

</details>

Challenge 5

function App() { return ( <div> {false && <p>Hello</p>} {null && <p>Hello</p>} {undefined && <p>Hello</p>} {0 && <p>Hello</p>} {'' && <p>Hello</p>} {NaN && <p>Hello</p>} </div> ); } // What renders on screen?
<details> <summary>Answer</summary>

Only 0 and NaN render on screen!

  • false β†’ renders nothing βœ…
  • null β†’ renders nothing βœ…
  • undefined β†’ renders nothing βœ…
  • 0 β†’ renders "0" on screen ⚠️
  • '' β†’ renders nothing βœ…
  • NaN β†’ renders "NaN" on screen ⚠️

React renders false, null, undefined, and empty strings as nothing. But 0 and NaN are rendered as text because they're valid numbers that React converts to strings.

</details>

🎯 Module 01 Summary

You now understand:

  • βœ… What React is and why it exists
  • βœ… JSX and how it compiles to JavaScript
  • βœ… Components (function and class)
  • βœ… Props and one-way data flow
  • βœ… State with useState
  • βœ… Event handling
  • βœ… Conditional rendering patterns
  • βœ… Lists and the key prop
  • βœ… Controlled vs uncontrolled components
  • βœ… The Virtual DOM and reconciliation
  • βœ… Common mistakes to avoid

Next up: Module 02: Hooks In Depth β€” where we'll master every React hook and learn to build custom hooks. This is where React gets really powerful. β†’