⚛️ React — Zero to Hero

Build modern UIs with the world's most popular JavaScript library

⚛️ What is React?

React is a JavaScript library by Meta for building user interfaces. It uses a component-based architecture and a virtual DOM for fast, efficient updates.

🧩 Components

Break UI into reusable, independent pieces.

🔄 Reactive State

UI re-renders automatically when data changes.

⚡ Virtual DOM

React diffs changes and updates only what's needed.

🌍 Huge Ecosystem

Used by Meta, Netflix, Airbnb, Twitter.

⚙️ Setup

# Create a new React app (Vite — recommended)
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run dev

# Or with Create React App
npx create-react-app my-app
cd my-app
npm start

🔷 JSX

JSX is HTML-like syntax inside JavaScript. It compiles to React.createElement() calls.

// JSX rules:
const el = (
  <div className="card">          // class → className
    <h1>Hello {name}</h1>       // JS in {}
    <img src="/logo.png" />     // self-closing
    {isLoggedIn && <p>Welcome!</p>} // conditional
  </div>
);

🧩 Components

// Function component (modern standard)
function Button({ label, onClick }) {
  return (
    <button onClick={onClick} className="btn">
      {label}
    </button>
  );
}

// Use it
function App() {
  return <Button label="Click me" onClick={() => alert('Hi!')} />;
}

📨 Props

Props are read-only inputs passed from parent to child — like HTML attributes.

function Card({ title, description, image }) {
  return (
    <div className="card">
      <img src={image} alt={title} />
      <h2>{title}</h2>
      <p>{description}</p>
    </div>
  );
}

// Default props
Card.defaultProps = { image: '/placeholder.png' };

🔄 useState

useState stores data that changes over time and triggers a re-render.

import { useState } from 'react';

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}
💡 Never mutate state directly. Always use the setter: setCount(count + 1), never count++.

💫 useEffect

Run side effects: fetch data, subscribe to events, update the DOM.

import { useState, useEffect } from 'react';

function Users() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    // Runs after render
    fetch('https://jsonplaceholder.typicode.com/users')
      .then(r => r.json())
      .then(data => setUsers(data));

    return () => { /* cleanup */ };
  }, []); // [] = run once on mount

  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}

🖱 Events

function Form() {
  const handleClick  = (e) => console.log('clicked', e);
  const handleChange = (e) => console.log(e.target.value);
  const handleSubmit = (e) => { e.preventDefault(); /* ... */ };

  return (
    <form onSubmit={handleSubmit}>
      <input onChange={handleChange} />
      <button onClick={handleClick}>Submit</button>
    </form>
  );
}

📋 Rendering Lists

const fruits = ['Apple', 'Banana', 'Cherry'];

function FruitList() {
  return (
    <ul>
      {fruits.map((fruit, index) => (
        <li key={index}>{fruit}</li>
      ))}
    </ul>
  );
}
💡 Always add a unique key prop when rendering lists. Use IDs from your data, not array indexes, when possible.

📝 Controlled Forms

function LoginForm() {
  const [email, setEmail] = useState('');
  const [pass,  setPass]  = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({ email, pass });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={email} onChange={e => setEmail(e.target.value)} type="email" />
      <input value={pass}  onChange={e => setPass(e.target.value)}  type="password" />
      <button>Login</button>
    </form>
  );
}

🌐 Context API

Share state globally without prop-drilling through every component.

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [dark, setDark] = useState(false);
  return (
    <ThemeContext.Provider value={{ dark, setDark }}>
      {children}
    </ThemeContext.Provider>
  );
}

// Consume anywhere
function Navbar() {
  const { dark, setDark } = useContext(ThemeContext);
  return <button onClick={() => setDark(!dark)}>{dark ? '☀️' : '🌙'}</button>;
}

🪝 Custom Hooks

// useFetch — reusable data fetching hook
function useFetch(url) {
  const [data,    setData]    = useState(null);
  const [loading, setLoading] = useState(true);
  const [error,   setError]   = useState(null);

  useEffect(() => {
    fetch(url)
      .then(r => r.json())
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false));
  }, [url]);

  return { data, loading, error };
}

// Usage
const { data, loading } = useFetch('/api/users');

🔗 React Router

# Install
npm install react-router-dom
import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/"        element={<Home />} />
        <Route path="/about"   element={<About />} />
        <Route path="/post/:id" element={<Post />} />
      </Routes>
    </BrowserRouter>
  );
}

function Post() {
  const { id } = useParams();
  return <h1>Post #{id}</h1>;
}

🚀 Pro Tips

// 1. useMemo — cache expensive calculations
const total = useMemo(() => items.reduce((s,i) => s+i.price, 0), [items]);

// 2. useCallback — stable function reference
const handleClick = useCallback(() => doSomething(id), [id]);

// 3. Lazy loading
const Dashboard = React.lazy(() => import('./Dashboard'));

// 4. Optional chaining in JSX
<p>{user?.profile?.bio ?? 'No bio'}</p>

// 5. Spread props
<Input {...register('email')} type="email" />
🎉 You now know React! Next: learn React Query for data fetching, Zustand for state management, and Next.js for full-stack apps.