Introduction

React is a powerful JavaScript library for building user interfaces. Created by Facebook, React has become the most popular choice for modern web development. It uses a component-based architecture and a virtual DOM for efficient rendering. In this lesson, you'll learn React fundamentals, hooks, state management, and how to build interactive applications.

Learning Objectives

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

  • Understand React components and JSX syntax
  • Manage component state with useState hook
  • Handle side effects with useEffect hook
  • Pass data between components using props
  • Handle events in React applications
  • Build a complete React application

Prerequisites

  • Completion of Lessons 01-05
  • Strong JavaScript fundamentals
  • Understanding of ES6+ features
  • Node.js and npm installed

Estimated Time

5 hours (including practice exercises)

Setting Up React

The easiest way to start a React project is using Create React App:

Terminal
# Create new React app
npx create-react-app my-app

# Navigate to project
cd my-app

# Start development server
npm start
Alternative: You can also use Vite for faster development: npm create vite@latest my-app -- --template react

JSX Basics

JSX is a syntax extension that looks like HTML but works in JavaScript:

JavaScript (JSX)
// Basic JSX
const element = <h1>Hello, React!</h1>;

// JSX with expressions
const name = 'Alice';
const greeting = <h1>Hello, {name}!</h1>;

// JSX with attributes
const image = <img src="photo.jpg" alt="Profile" />;

// JSX with children
const card = (
  <div className="card">
    <h2>Title</h2>
    <p>Description</p>
  </div>
);

// JSX expressions
const isLoggedIn = true;
const message = (
  <div>
    {isLoggedIn ? <p>Welcome back!</p> : <p>Please log in</p>}
  </div>
);

// JSX with arrays
const numbers = [1, 2, 3, 4, 5];
const listItems = (
  <ul>
    {numbers.map(num => <li key={num}>{num}</li>)}
  </ul>
);
Important: Use className instead of class, and htmlFor instead of for in JSX.

Function Components

Modern React uses function components with hooks:

JavaScript
// Simple component
function Welcome() {
  return <h1>Hello, World!</h1>;
}

// Component with props
function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

// Destructured props
function UserCard({ name, email, avatar }) {
  return (
    <div className="user-card">
      <img src={avatar} alt={name} />
      <h3>{name}</h3>
      <p>{email}</p>
    </div>
  );
}

// Using components
function App() {
  return (
    <div>
      <Welcome />
      <Greeting name="Alice" />
      <UserCard 
        name="Bob" 
        email="[email protected]"
        avatar="avatar.jpg"
      />
    </div>
  );
}

useState Hook

useState allows you to add state to function components:

JavaScript
import { useState } from 'react';

// Simple counter
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setCount(count - 1)}>
        Decrement
      </button>
      <button onClick={() => setCount(0)}>
        Reset
      </button>
    </div>
  );
}

// Multiple state variables
function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [age, setAge] = useState(0);
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({ name, email, age });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Name"
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Email"
      />
      <input
        type="number"
        value={age}
        onChange={(e) => setAge(Number(e.target.value))}
        placeholder="Age"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

// Object state
function UserProfile() {
  const [user, setUser] = useState({
    name: 'Alice',
    age: 25,
    email: '[email protected]'
  });
  
  const updateName = (newName) => {
    setUser(prevUser => ({
      ...prevUser,
      name: newName
    }));
  };
  
  return (
    <div>
      <h2>{user.name}</h2>
      <p>Age: {user.age}</p>
      <p>Email: {user.email}</p>
      <button onClick={() => updateName('Bob')}>
        Change Name
      </button>
    </div>
  );
}

// Array state
function TodoList() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');
  
  const addTodo = () => {
    if (input.trim()) {
      setTodos([...todos, { id: Date.now(), text: input }]);
      setInput('');
    }
  };
  
  const removeTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  
  return (
    <div>
      <input
        value={input}
        onChange={(e) => setInput(e.target.value)}
        placeholder="Add todo"
      />
      <button onClick={addTodo}>Add</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            {todo.text}
            <button onClick={() => removeTodo(todo.id)}>
              Delete
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

useEffect Hook

useEffect handles side effects like data fetching, subscriptions, and DOM manipulation:

JavaScript
import { useState, useEffect } from 'react';

// Run once on mount
function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(res => res.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, []); // Empty array = run once
  
  if (loading) return <p>Loading...</p>;
  return <div>{JSON.stringify(data)}</div>;
}

// Run when dependency changes
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch(`https://api.example.com/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]); // Re-run when userId changes
  
  return user ? <div>{user.name}</div> : <p>Loading...</p>;
}

// Cleanup function
function Timer() {
  const [seconds, setSeconds] = useState(0);
  
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
    
    // Cleanup
    return () => clearInterval(interval);
  }, []);
  
  return <p>Seconds: {seconds}</p>;
}

// Multiple effects
function Dashboard() {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);
  
  // Fetch user
  useEffect(() => {
    fetch('/api/user')
      .then(res => res.json())
      .then(data => setUser(data));
  }, []);
  
  // Fetch posts when user loads
  useEffect(() => {
    if (user) {
      fetch(`/api/posts?userId=${user.id}`)
        .then(res => res.json())
        .then(data => setPosts(data));
    }
  }, [user]);
  
  return (
    <div>
      {user && <h2>{user.name}</h2>}
      <ul>
        {posts.map(post => <li key={post.id}>{post.title}</li>)}
      </ul>
    </div>
  );
}

Event Handling

JavaScript
function EventExamples() {
  const [text, setText] = useState('');
  
  // Click event
  const handleClick = () => {
    alert('Button clicked!');
  };
  
  // Input change
  const handleChange = (e) => {
    setText(e.target.value);
  };
  
  // Form submit
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Submitted:', text);
  };
  
  // Mouse events
  const handleMouseEnter = () => {
    console.log('Mouse entered');
  };
  
  // Keyboard events
  const handleKeyPress = (e) => {
    if (e.key === 'Enter') {
      console.log('Enter pressed');
    }
  };
  
  return (
    <div>
      <button onClick={handleClick}>Click Me</button>
      
      <form onSubmit={handleSubmit}>
        <input
          value={text}
          onChange={handleChange}
          onKeyPress={handleKeyPress}
          placeholder="Type something"
        />
        <button type="submit">Submit</button>
      </form>
      
      <div 
        onMouseEnter={handleMouseEnter}
        style={{ padding: '20px', background: '#f0f0f0' }}
      >
        Hover over me
      </div>
    </div>
  );
}

Conditional Rendering

JavaScript
function ConditionalExamples() {
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  // If-else with ternary
  return (
    <div>
      {isLoggedIn ? (
        <p>Welcome back!</p>
      ) : (
        <p>Please log in</p>
      )}
      
      {/* Logical AND */}
      {user && <p>Hello, {user.name}</p>}
      
      {/* Multiple conditions */}
      {loading ? (
        <p>Loading...</p>
      ) : user ? (
        <UserProfile user={user} />
      ) : (
        <p>No user found</p>
      )}
      
      {/* Conditional classes */}
      <button className={isLoggedIn ? 'active' : 'inactive'}>
        Status
      </button>
    </div>
  );
}

Lists and Keys

JavaScript
function ProductList() {
  const products = [
    { id: 1, name: 'Laptop', price: 999 },
    { id: 2, name: 'Phone', price: 699 },
    { id: 3, name: 'Tablet', price: 499 }
  ];
  
  return (
    <div>
      <h2>Products</h2>
      <ul>
        {products.map(product => (
          <li key={product.id}>
            {product.name} - ${product.price}
          </li>
        ))}
      </ul>
    </div>
  );
}

// Component for list items
function ProductItem({ product }) {
  return (
    <div className="product-card">
      <h3>{product.name}</h3>
      <p>${product.price}</p>
      <button>Add to Cart</button>
    </div>
  );
}

function ProductGrid() {
  const products = [...]; // product array
  
  return (
    <div className="grid">
      {products.map(product => (
        <ProductItem key={product.id} product={product} />
      ))}
    </div>
  );
}
Important: Always provide a unique key prop when rendering lists. Never use array index as key if items can be reordered.

Practical Example: Todo App

JavaScript
import { useState } from 'react';

function TodoApp() {
  const [todos, setTodos] = useState([]);
  const [input, setInput] = useState('');
  const [filter, setFilter] = useState('all');
  
  const addTodo = () => {
    if (input.trim()) {
      setTodos([
        ...todos,
        {
          id: Date.now(),
          text: input,
          completed: false
        }
      ]);
      setInput('');
    }
  };
  
  const toggleTodo = (id) => {
    setTodos(todos.map(todo =>
      todo.id === id
        ? { ...todo, completed: !todo.completed }
        : todo
    ));
  };
  
  const deleteTodo = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };
  
  const filteredTodos = todos.filter(todo => {
    if (filter === 'active') return !todo.completed;
    if (filter === 'completed') return todo.completed;
    return true;
  });
  
  const stats = {
    total: todos.length,
    active: todos.filter(t => !t.completed).length,
    completed: todos.filter(t => t.completed).length
  };
  
  return (
    <div className="todo-app">
      <h1>Todo List</h1>
      
      {/* Input */}
      <div className="input-section">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && addTodo()}
          placeholder="What needs to be done?"
        />
        <button onClick={addTodo}>Add</button>
      </div>
      
      {/* Filters */}
      <div className="filters">
        <button 
          onClick={() => setFilter('all')}
          className={filter === 'all' ? 'active' : ''}
        >
          All ({stats.total})
        </button>
        <button 
          onClick={() => setFilter('active')}
          className={filter === 'active' ? 'active' : ''}
        >
          Active ({stats.active})
        </button>
        <button 
          onClick={() => setFilter('completed')}
          className={filter === 'completed' ? 'active' : ''}
        >
          Completed ({stats.completed})
        </button>
      </div>
      
      {/* Todo List */}
      <ul className="todo-list">
        {filteredTodos.map(todo => (
          <li 
            key={todo.id}
            className={todo.completed ? 'completed' : ''}
          >
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
            />
            <span onClick={() => toggleTodo(todo.id)}>
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>
              Delete
            </button>
          </li>
        ))}
      </ul>
      
      {/* Empty state */}
      {filteredTodos.length === 0 && (
        <p className="empty-state">No todos to display</p>
      )}
    </div>
  );
}

export default TodoApp;

Key Takeaways

  • React uses a component-based architecture
  • JSX combines HTML-like syntax with JavaScript
  • useState manages component state
  • useEffect handles side effects and lifecycle
  • Props pass data from parent to child components
  • Always provide unique keys when rendering lists
  • Event handlers use camelCase (onClick, onChange)
  • State updates are asynchronous

Practice Exercises

Exercise 1: Counter with History

Build a counter that tracks all previous values:

Task
// Create a counter that:
// 1. Shows current count
// 2. Has increment/decrement buttons
// 3. Displays history of all values
// 4. Can undo to previous value

Exercise 2: User Search

Fetch and filter users from an API:

Task
// Build a user search that:
// 1. Fetches users from JSONPlaceholder
// 2. Filters users as you type
// 3. Shows loading state
// 4. Handles errors gracefully

Exercise 3: Shopping Cart

Create a shopping cart with add/remove functionality:

Task
// Build a cart that:
// 1. Displays products
// 2. Adds items to cart
// 3. Updates quantities
// 4. Calculates total price
// 5. Removes items

What's Next?

In Lesson 07, you'll shift focus to backend development with Node.js and Express. You'll learn to create HTTP servers, implement routing, use middleware, and build RESTful API endpoints.