Skip to main content

Command Palette

Search for a command to run...

The Ultimate React Guide

Updated
28 min read
The Ultimate React Guide

Basics of React JS


🌱 Chapter 1: What Is React — And Why Should You Care?

Imagine you’re an architect designing a futuristic city. You wouldn’t build every house brick-by-brick from scratch. Instead, you’d design reusable blueprints — modular homes that can be assembled, customized, and scaled effortlessly.

That’s React.

React is not a framework. It’s a JavaScript library for building user interfaces — specifically, dynamic, component-driven UIs that respond instantly to user input, data changes, and real-time events.

Created by Facebook and now maintained by a global community, React powers everything from Instagram to Netflix, Airbnb to Dropbox — and even NASA’s mission dashboards.

Why React Wins

  • Component-Based Architecture → Build once, reuse everywhere.

  • Virtual DOM → Updates only what changed → lightning-fast performance.

  • Declarative Syntax → Describe what the UI should look like, not how to change it.

  • Rich Ecosystem → React Router, Context API, Redux, TanStack Query, Next.js, Remix — tools for every scale.

  • Career Rocket Fuel → The most in-demand front-end skill globally.

💡 Think of React as LEGO for developers. Each component is a LEGO block. Snap them together, and you build entire worlds.


⚙️ Chapter 2: Setting Up Your React Playground

You don’t need a fancy studio to start painting. Similarly, you don’t need complex setups to begin with React.

The fastest, cleanest way today? Vite.

npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev

Open http://localhost:5173 — boom. You’re live.

🐢 Why not Create React App (CRA)?
CRA is the old veteran — reliable but slow. Vite is the new champion: near-instant startup, Hot Module Replacement (HMR), and modern tooling out of the box. For new projects, Vite wins.

Your project structure:

src/
├── main.jsx       → Entry point
├── App.jsx        → Root component
└── components/    → Your reusable LEGO blocks

🧩 Chapter 3: Components — Your Reusable LEGO Blocks

In React, everything is a component.

A button. A navbar. A card. A modal. An entire dashboard.

Components are JavaScript functions that return JSX — a syntax extension that lets you write HTML-like code inside JavaScript.

Functional Components — The Modern Standard

// src/components/WelcomeBanner.jsx
export default function WelcomeBanner({ name, role }) {
  return (
    <div className="banner">
      <h1>Welcome back, {name}!</h1>
      <p>You’re logged in as: {role}</p>
    </div>
  );
}

Use it anywhere:

// App.jsx
import WelcomeBanner from './components/WelcomeBanner';

function App() {
  return (
    <div className="app">
      <WelcomeBanner name="Alex" role="Admin" />
    </div>
  );
}

📌 Rule of Thumb: Always name components in PascalCase (UserProfile, DataTable). Files should match (UserProfile.jsx).


🎁 Chapter 4: Props — Passing Data Like Birthday Gifts

Props (short for “properties”) are how you pass data from a parent component to a child — like handing a wrapped gift to a friend.

They are read-only. The child can use them, display them, compute with them — but never mutate them.

// Parent
<UserProfile name="Jordan" age={30} location="Berlin" />

// Child
function UserProfile({ name, age, location }) {
  return (
    <div className="profile">
      <h2>{name}</h2>
      <p>{age} years old</p>
      <p>Lives in {location}</p>
    </div>
  );
}

💡 Destructuring props at the function signature isn’t just clean — it’s expected in professional codebases.


🧠 Chapter 5: State — The Beating Heart of Your App

If props are gifts you receive, state is your personal diary — private, mutable, and triggering re-renders when updated.

State holds data that changes over time: form inputs, counters, loading statuses, lists of todos.

useState — The Foundation Hook

import { useState } from 'react';

function LikeButton() {
  const [likes, setLikes] = useState(0); // Initial state = 0

  return (
    <button onClick={() => setLikes(likes + 1)}>
      👍 {likes} Likes
    </button>
  );
}

Every time setLikes is called, React re-renders the component with the new value.

⚠️ Golden Rule: Never mutate state directly. Always use the setter.


🔄 Chapter 6: Updating Objects and Arrays in State — The Immutable Way

Here’s where many beginners stumble.

React relies on shallow comparisons to detect state changes. If you mutate an object or array directly, React won’t know anything changed — and your UI stays stale.

❌ Wrong — Mutating State

const [user, setUser] = useState({ name: "Sam", age: 25 });

// DON’T DO THIS
user.age = 26;
setUser(user); // React sees same object reference → no re-render!

✅ Correct — Immutably Update

// Update object
setUser(prev => ({ ...prev, age: 26 }));

// Add to array
const [todos, setTodos] = useState([]);
setTodos(prev => [...prev, newTodo]);

// Update item in array
setTodos(prev => 
  prev.map(todo => 
    todo.id === targetId ? { ...todo, completed: true } : todo
  )
);

// Remove from array
setTodos(prev => prev.filter(todo => todo.id !== targetId));

🧊 Think of state like ice sculptures. You don’t reshape the existing one — you melt it down and carve a brand new sculpture. That’s immutability.


⏳ Chapter 7: useEffect — Handling Side Effects Gracefully

Some things don’t belong in the render cycle: fetching data, setting up subscriptions, manually changing the DOM. These are called side effects.

Enter useEffect.

import { useEffect, useState } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    // This runs AFTER the component renders
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      });
  }, [userId]); // Dependency array — re-run if userId changes

  if (loading) return <p>Loading...</p>;
  return <div>Welcome, {user.name}!</div>;
}

The Dependency Array — Your Control Panel

  • [] → Run once after initial render (like componentDidMount).

  • [a, b] → Re-run if a or b changes.

  • No array → Run after every render (usually a mistake).

🔄 Analogy: useEffect is like a smart home system. It watches specific sensors (dependencies) and triggers actions (side effects) only when those sensors detect change.


🧵 Chapter 8: Custom Hooks — Reusable Logic Superpowers

Custom hooks let you extract component logic into reusable functions — complete with their own state, effects, and more.

Name them starting with use.

// hooks/useLocalStorage.js
import { useState, useEffect } from 'react';

export function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

Use it anywhere:

function ThemeSwitcher() {
  const [darkMode, setDarkMode] = useLocalStorage('darkMode', false);

  return (
    <button onClick={() => setDarkMode(!darkMode)}>
      Toggle {darkMode ? 'Light' : 'Dark'} Mode
    </button>
  );
}

🦸 Custom hooks are your secret weapon for avoiding repetitive logic — auth checks, form handlers, API clients, etc.


🌐 Chapter 9: Context API — Global State Without the Headache

Need to share data across many components? (e.g., user auth, theme, language). Props drilling — passing props through 5 layers of components — becomes messy.

Context API to the rescue.

Step 1: Create Context

// context/AuthContext.jsx
import { createContext, useContext, useState } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);

  const login = (userData) => setUser(userData);
  const logout = () => setUser(null);

  return (
    <AuthContext.Provider value={{ user, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) throw new Error('useAuth must be used within AuthProvider');
  return context;
}

Step 2: Wrap Your App

// main.jsx
import { AuthProvider } from './context/AuthContext';

ReactDOM.createRoot(document.getElementById('root')).render(
  <AuthProvider>
    <App />
  </AuthProvider>
);

Step 3: Use Anywhere

// Navbar.jsx
import { useAuth } from '../context/AuthContext';

function Navbar() {
  const { user, logout } = useAuth();

  return (
    <nav>
      {user ? (
        <>
          <span>Welcome, {user.name}</span>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <LoginButton />
      )}
    </nav>
  );
}

🗺️ Think of Context as a public bulletin board in your app. Any component can pin something to it (Provider) or read from it (useContext).


🧭 Chapter 10: React Router — Navigating Your App Like a Pro

Single Page Applications (SPAs) need client-side routing. Enter React Router v6+.

Install:

npm install react-router-dom

Basic Setup

// App.jsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import Dashboard from './pages/Dashboard';
import NotFound from './pages/NotFound';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="*" element={<NotFound />} /> {/* 404 */}
      </Routes>
    </BrowserRouter>
  );
}

Nested Routes & useParams

<Route path="/users" element={<UsersLayout />}>
  <Route index element={<UsersList />} />
  <Route path=":id" element={<UserProfile />} />
</Route>

// Inside UserProfile.jsx
import { useParams } from 'react-router-dom';

function UserProfile() {
  const { id } = useParams(); // Get URL parameter
  // Fetch user with id...
}

🧭 React Router turns your app into a multi-page experience without actual page reloads — seamless, fast, and user-friendly.


🧪 Chapter 11: Advanced Patterns — Refs, Memo, and useCallback

useRef — Accessing the DOM or Persisting Values

Need to focus an input, measure a div, or store a value that doesn’t trigger re-renders? Use useRef.

function TextInputWithFocusButton() {
  const inputRef = useRef(null);

  const handleClick = () => {
    inputRef.current.focus(); // Direct DOM access
  };

  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Focus Input</button>
    </>
  );
}

🔌 Think of useRef as a USB drive you plug into your component — stores data across renders without causing updates.


useMemo & useCallback — Performance Optimizations

When you have expensive calculations or want to prevent unnecessary re-renders of child components, these hooks help.

const expensiveValue = useMemo(() => {
  return computeExpensiveValue(a, b);
}, [a, b]);

const handleClick = useCallback(() => {
  doSomething(c, d);
}, [c, d]);

Pass handleClick to a memoized child — it won’t re-render unless dependencies change.

⚖️ Use sparingly. Premature optimization is the root of all evil. Only optimize when you measure a performance issue.


🏁 Final Checklist: Are You React-Advanced Ready?

✅ You build apps with functional components and hooks
✅ You manage state with useState and update it immutably
✅ You handle side effects cleanly with useEffect
✅ You’ve created and used custom hooks
✅ You share global state with Context API
✅ You navigate with React Router
✅ You optimize performance with useMemo/useCallback
✅ You interact with the DOM via useRef


Advanced React JS



🔐 Chapter 1: Authentication — Securing Your App Like a Pro

Imagine your app is a high-security building. Not everyone gets in. Some need keycards (JWT). Some get escorted to specific floors (Protected Routes). Others are turned away at the lobby (Login Redirects).

That’s modern React authentication.

🧩 The JWT Flow — Your Digital Keycard

  1. User submits email/password → POST to /api/login

  2. Server validates → returns a JWT (JSON Web Token)

  3. Client stores JWT (in localStorage or httpOnly cookie)

  4. Every subsequent request → Attach JWT in Authorization: Bearer <token>

  5. Server verifies token → Grants/Denies access

// utils/auth.js
export const login = async (email, password) => {
  const res = await fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  });
  const data = await res.json();
  if (res.ok) {
    localStorage.setItem('token', data.token); // 🔐 Store JWT
    return data.user;
  }
  throw new Error(data.message);
};

export const getToken = () => localStorage.getItem('token');
export const logout = () => localStorage.removeItem('token');

⚠️ Security Note: For higher security, use httpOnly cookies (set by server) instead of localStorage — immune to XSS. localStorage is simpler for learning.


🚧 Protected Routes — The Bouncer at the Door

Not all pages are public. Dashboard? Profile? Settings? Only for logged-in users.

Create a <ProtectedRoute> wrapper:

// components/ProtectedRoute.jsx
import { Navigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';

export default function ProtectedRoute({ children }) {
  const { user } = useAuth();
  if (!user) {
    return <Navigate to="/login" replace />;
  }
  return children;
}

Use it in your routes:

// App.jsx
<Route path="/dashboard" element={
  <ProtectedRoute>
    <Dashboard />
  </ProtectedRoute>
} />

🎯 Pro Tip: Redirect users back to their intended page after login using useLocation() and state.


🔄 Login Flow — The Full User Journey

  1. User lands on /login

  2. Enters credentials → Clicks “Sign In”

  3. On success → Save token + user → Redirect to /dashboard

  4. On every page load → Check token validity → Set user in context

  5. User clicks “Logout” → Clear token → Redirect to /login

// context/AuthContext.jsx (enhanced)
useEffect(() => {
  const token = getToken();
  if (token) {
    // Optional: Validate token with backend or decode locally
    const user = decodeToken(token); // jwt-decode library
    setUser(user);
  }
}, []);

🧠 Analogy: Authentication is like a concert wristband. Get it at the entrance (login), show it at every checkpoint (protected route), lose it — you’re out (logout).


📡 Chapter 2: Data Fetching — Beyond useEffect (SWR & TanStack Query)

Forget useEffect + fetch. That’s the bicycle. SWR and TanStack Query (React Query) are the sports cars — built for real apps with caching, background updates, pagination, and mutations.

⚡ Why You Need a Data Fetching Library

  • Automatic Caching → No duplicate requests

  • Background Refetching → Data stays fresh

  • Pagination & Infinite Scroll → Built-in

  • Optimistic Updates → UI feels instant

  • Error + Loading States → Unified handling


🌟 Option 1: SWR — Simple, Fast, React-First

Install:

npm install swr

Basic Usage:

import useSWR from 'swr';

const fetcher = (url) => fetch(url).then(r => r.json());

function UserProfile({ userId }) {
  const { data, error, isLoading } = useSWR(`/api/users/${userId}`, fetcher);

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error loading user</div>;

  return <div>Hello, {data.name}!</div>;
}

Mutations (Updating Data):

import { mutate } from 'swr';

// After updating user on server
mutate(`/api/users/${userId}`); // Revalidate and refetch

🐦 SWR = “Stale-While-Revalidate” — show old data while fetching new in background. Perfect for great UX.


🚀 Option 2: TanStack Query — The Enterprise Powerhouse

Install:

npm install @tanstack/react-query

Setup Provider:

// main.jsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

ReactDOM.createRoot(root).render(
  <QueryClientProvider client={queryClient}>
    <App />
  </QueryClientProvider>
);

Fetching Data:

import { useQuery, useMutation } from '@tanstack/react-query';

function Todos() {
  const { data, isLoading } = useQuery({
    queryKey: ['todos'],
    queryFn: () => fetch('/api/todos').then(res => res.json())
  });

  const deleteTodo = useMutation({
    mutationFn: (id) => fetch(`/api/todos/${id}`, { method: 'DELETE' }),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['todos'] }); // 🔄 Refetch
    }
  });

  return (
    <div>
      {data?.map(todo => (
        <div key={todo.id}>
          {todo.text}
          <button onClick={() => deleteTodo.mutate(todo.id)}>Delete</button>
        </div>
      ))}
    </div>
  );
}

💥 TanStack Query gives you devtools, prefetching, retries, and more. Use it for complex apps.


🧱 Chapter 3: Project Architecture — Scaling Beyond “src/App.jsx”

When your app grows beyond 3 components, folder chaos begins. Here’s the battle-tested structure used at Turing and top startups.

src/
├── assets/            → Images, fonts, styles
├── components/        → Reusable UI (Button, Card, Modal)
│   ├── ui/            → Base components (from shadcn/ui or similar)
│   └── layout/        → AppShell, Header, Sidebar
├── pages/             → Route-level components (LoginPage, DashboardPage)
├── hooks/             → Custom hooks (useAuth, useLocalStorage, useApi)
├── context/           → Global state (AuthContext, ThemeContext)
├── services/          → API clients (api.js, authService.js, todoService.js)
├── utils/             → Helpers (formatDate.js, validators.js)
├── routes/            → Route definitions + ProtectedRoute wrappers
├── App.jsx            → Routes + Providers
└── main.jsx           → Entry + QueryClientProvider, AuthProvider, etc.

🏗️ Think of your app like a city:

  • components/ = Buildings (reusable)

  • pages/ = Districts (unique combinations)

  • services/ = Utilities (power, water, data)

  • hooks/ = City ordinances (rules everyone follows)


🛡️ Error Boundaries — Graceful Degradation

Components crash. APIs fail. Networks drop. Your app shouldn’t show a blank white screen.

Error Boundaries catch JavaScript errors in child components and display fallback UI.

// components/ErrorBoundary.jsx
import { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    console.error("Caught an error:", error, errorInfo);
    // Log to error reporting service (Sentry, LogRocket)
  }

  render() {
    if (this.state.hasError) {
      return (
        <div className="error-fallback">
          <h2>Something went wrong.</h2>
          <button onClick={() => window.location.reload()}>Refresh</button>
        </div>
      );
    }
    return this.props.children;
  }
}

export default ErrorBoundary;

Wrap it around route components:

// App.jsx
<Route path="/dashboard" element={
  <ErrorBoundary>
    <Dashboard />
  </ErrorBoundary>
} />

🧯 Analogy: Error Boundaries are firewalls. They contain the blaze so the whole building doesn’t burn down.


🚀 Chapter 4: Deployment — From Localhost to the World

Your app works on your machine. Now let’s ship it.

Option 1: Static Hosting (Vercel, Netlify) — For Most React Apps

If your React app is client-side rendered (CSR) and talks to a separate backend API — this is perfect.

Steps:

  1. Build your app: npm run build

  2. Drag-and-drop dist/ or build/ folder to Vercel/Netlify

  3. Done. You get HTTPS, CDN, and global edge network.

✅ Zero config. Free tier available. Ideal for portfolios, dashboards, marketing sites.


Option 2: Docker + VM (AWS EC2, GCP Compute Engine) — Full Control

Need to run your React app alongside a Node.js backend? Or want full infrastructure control? Dockerize it.

Step 1: Dockerfile

# Dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Step 2: nginx.conf (for client-side routing)

server {
    listen 80;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
        try_files $uri $uri/ /index.html; # ← Crucial for React Router
    }
}

Step 3: Build & Run

docker build -t my-react-app .
docker run -d -p 80:80 my-react-app

Deploy this image to any VM or Kubernetes cluster.

🐳 Docker is your app’s shipping container — identical environment everywhere: your laptop, CI server, or cloud VM.


🌐 Bonus: Environment Variables

Never hardcode API keys or URLs.

Create .env file:

VITE_API_BASE_URL=https://api.yourapp.com
VITE_APP_NAME=My Awesome App

Access in code:

const apiUrl = import.meta.env.VITE_API_BASE_URL;

🔐 Prefix with VITE_ (in Vite) or REACT_APP_ (in CRA) to expose to client. Never put secrets here — only public config.


🧪 Chapter 5: Testing & Debugging — Ship with Confidence

React DevTools & TanStack Query Devtools

Install browser extensions. Inspect component hierarchy, hooks, state, and query cache in real-time.

Enable TanStack Query Devtools:

// Only in development
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

function App() {
  return (
    <>
      {/* Your app */}
      {import.meta.env.DEV && <ReactQueryDevtools />}
    </>
  );
}

Basic Testing with Vitest + React Testing Library

Install:

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom

Example test:

// __tests__/Button.test.jsx
import { render, screen, fireEvent } from '@testing-library/react';
import Button from '../components/Button';

test('calls onClick when clicked', () => {
  const handleClick = vi.fn(); // Mock function
  render(<Button onClick={handleClick}>Click Me</Button>);
  fireEvent.click(screen.getByText(/click me/i));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

Run: npm run test

🧪 Testing isn’t optional in professional apps. Start small — test critical components and flows.


🏁 Final Checklist: Are You Production-Ready?

✅ You implement JWT auth with protected routes
✅ You fetch data with SWR or TanStack Query — not just useEffect
✅ Your folder structure scales beyond 10 components
✅ You use Error Boundaries to prevent full-app crashes
✅ You can Dockerize and deploy your app to a VM
✅ You use environment variables for config
✅ You’ve peeked at DevTools and written a basic test


➡️ What’s Next? Full-Stack, TypeScript, or State Machines?

You’ve crossed the threshold. You’re no longer a React beginner — you’re a React Engineer.

Next, consider:

  • 🧑‍💻 Full-Stack React — Build your own backend with Node.js + Express or Django

  • 🔤 TypeScript — Add static typing for fewer bugs and better DX

  • 🧭 State Machines (XState) — For complex UI flows (onboarding, checkout)

  • 📱 React Native — Take your skills to mobile

But first — build, deploy, and share something real.

Q&A


🧱 CHAPTER 1: BASICS & CORE CONCEPTS

Q1: What is React? Why use it?

Answer:
React is a JavaScript library (not a framework) for building user interfaces, especially dynamic, component-based UIs. Created by Facebook, it’s used by Instagram, Netflix, Airbnb, etc.

Why React?

  • Component-Based: Reusable, modular UI blocks.

  • Virtual DOM: Efficient updates → fast performance.

  • Declarative: Describe what UI should look like, not how to change it.

  • Rich Ecosystem: React Router, Context, Redux, Next.js, etc.

  • High Demand: #1 front-end skill in job market.

💡 Analogy: React is like LEGO — snap together components to build complex UIs.


Q2: What is JSX?

Answer:
JSX = JavaScript XML. It’s a syntax extension that lets you write HTML-like code in JavaScript.

Example:

const element = <h1>Hello, {name}!</h1>;

Under the hood, JSX compiles to React.createElement() calls.

⚠️ Note: Browsers don’t understand JSX — you need a transpiler like Babel.


Q3: How do you create a React component?

Answer:
Two ways:

  1. Functional Component (Modern Standard):

     function Welcome({ name }) {
       return <h1>Hello, {name}</h1>;
     }
    
  2. Class Component (Legacy):

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

Always use Functional Components + Hooks unless maintaining legacy code.


Q4: What are Props?

Answer:
Props (short for properties) are read-only data passed from parent to child component.

Example:

<UserProfile name="Alex" age={25} />

function UserProfile({ name, age }) {
  return <div>{name}, {age} years old</div>;
}

🚫 Never mutate props — they’re immutable. Use state for mutable data.


Q5: What is State? How is it different from Props?

Answer:

  • Props: Passed from parent → child. Read-only. For configuration.

  • State: Managed within component. Mutable. Triggers re-render on change. For dynamic data (e.g., form inputs, counters).

Use useState hook:

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

🧩 CHAPTER 2: COMPONENTS & HOOKS

Q6: What is the useState Hook?

Answer:
useState lets you add state to functional components.

Syntax:

const [state, setState] = useState(initialValue);

Example:

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

⚠️ Golden Rule: Never mutate state directly — always use the setter.


Q7: How to update objects/arrays in state?

Answer:
React uses shallow comparison. Mutating state directly won’t trigger re-render.

❌ Wrong:

user.age = 26; setUser(user); // ❌ Same reference → no re-render

✅ Correct (Immutably):

// Object
setUser(prev => ({ ...prev, age: 26 }));

// Array - Add
setTodos(prev => [...prev, newTodo]);

// Array - Update
setTodos(prev => prev.map(t => t.id === id ? { ...t, done: true } : t));

// Array - Remove
setTodos(prev => prev.filter(t => t.id !== id));

🧊 Analogy: State is like ice — melt & recast, don’t reshape.


Q8: What is useEffect? Explain dependency array.

Answer:
useEffect handles side effects (data fetching, subscriptions, DOM changes).

Syntax:

useEffect(() => {
  // Side effect code
}, [dependencies]); // Dependency array
  • [] → Runs once after mount (like componentDidMount).

  • [a, b] → Re-runs if a or b changes.

  • No array → Runs after every render (usually a bug).

Example:

useEffect(() => {
  fetchUser(userId);
}, [userId]); // Re-fetch if userId changes

Q9: What are Custom Hooks? Give an example.

Answer:
Custom hooks extract reusable logic (with state/effects) into functions. Must start with use.

Example: useLocalStorage

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

// Usage
const [darkMode, setDarkMode] = useLocalStorage('darkMode', false);

Q10: What is useRef? When to use it?

Answer:
useRef gives you a mutable object that persists across renders without triggering re-renders.

Use cases:

  • Accessing DOM elements (focus, measure).

  • Storing mutable values (timers, previous state).

Example:

const inputRef = useRef();

useEffect(() => {
  inputRef.current.focus(); // Focus input on mount
}, []);

return <input ref={inputRef} />;

🌐 CHAPTER 3: STATE MANAGEMENT & ROUTING

Q11: What is Context API? Why use it?

Answer:
Context API shares global state (theme, user, language) without “prop drilling”.

Steps:

  1. Create Context: createContext()

  2. Wrap app with Provider

  3. Consume with useContext

Example:

const ThemeContext = createContext();

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  const theme = useContext(ThemeContext); // "dark"
  return <div className={theme}>...</div>;
}

Q12: How to implement Protected Routes?

Answer:
Create a ProtectedRoute wrapper that checks auth state and redirects if needed.

function ProtectedRoute({ children }) {
  const { user } = useAuth();
  if (!user) return <Navigate to="/login" replace />;
  return children;
}

// Usage
<Route path="/dashboard" element={
  <ProtectedRoute><Dashboard /></ProtectedRoute>
} />

Q13: How does React Router v6 work?

Answer:
Key components:

  • BrowserRouter: Wraps app.

  • Routes + Route: Define paths.

  • useParams: Access dynamic params (/users/:id).

  • useNavigate: Programmatic navigation.

Example:

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/users/:id" element={<UserProfile />} />
  <Route path="*" element={<NotFound />} />
</Routes>

// In UserProfile
const { id } = useParams();

CHAPTER 4: PERFORMANCE & ADVANCED PATTERNS

Q14: What is useMemo? When to use it?

Answer:
useMemo memoizes expensive calculations to avoid re-computing on every render.

Syntax:

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

✅ Use when:

  • Expensive computation (sorting, filtering large arrays).

  • Passing value to memoized child component.

⚠️ Don’t overuse — only optimize when you measure a performance issue.


Q15: What is useCallback? Why use it?

Answer:
useCallback memoizes functions to prevent unnecessary re-renders of child components.

Syntax:

const handleClick = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

✅ Use when:

  • Passing callback to optimized child (e.g., React.memo).

  • Dependency in useEffect.


Q16: What is React.memo? How is it different from useMemo?

Answer:

  • React.memo: Higher-Order Component that memoizes entire component. Prevents re-render if props unchanged.

  • useMemo: Memoizes values (strings, objects, arrays).

Example:

const MemoizedComponent = React.memo(MyComponent);

// Only re-renders if props.a or props.b change
<MemoizedComponent a={a} b={b} />

Q17: What are Error Boundaries? How to create one?

Answer:
Error Boundaries catch JavaScript errors in child components and display fallback UI (instead of blank screen).

Only works in class components (for now):

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    logErrorToService(error, info); // e.g., Sentry
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

// Usage
<ErrorBoundary><MyComponent /></ErrorBoundary>

📡 CHAPTER 5: DATA FETCHING & AUTH

Q18: How to fetch data in React? Problems with useEffect + fetch?

Answer:
useEffect + fetch works but lacks:

  • Caching

  • Background refetching

  • Pagination

  • Loading/error states

  • Optimistic updates

✅ Use SWR or TanStack Query instead.


Q19: Compare SWR vs TanStack Query.

Answer:

FeatureSWRTanStack Query
PhilosophySimple, React-firstEnterprise, feature-rich
Caching✅ Automatic✅ Advanced
DevTools✅ Built-in
MutationsBasic (mutate)Advanced (useMutation)
Pagination
Bundle SizeSmallerLarger
Learning CurveGentleSteeper

SWR for simple apps. TanStack Query for complex data needs.


Q20: How does JWT authentication work in React?

Answer:

  1. User logs in → POST /login → server returns JWT.

  2. Store JWT in localStorage or httpOnly cookie.

  3. Attach Authorization: Bearer <token> to API requests.

  4. Validate token on server for protected routes.

  5. Logout → remove token.

⚠️ Security: Prefer httpOnly cookies (immune to XSS) over localStorage.


🏗️ CHAPTER 6: ARCHITECTURE & DEPLOYMENT

Q21: What’s a scalable React project structure?

Answer:

src/
├── assets/          # Images, fonts
├── components/      # Reusable UI (Button, Card)
│   ├── ui/          # Base components (shadcn/ui)
│   └── layout/      # Header, Sidebar
├── pages/           # Route-level components
├── hooks/           # Custom hooks
├── context/         # Global state
├── services/        # API clients
├── utils/           # Helpers (formatDate, validators)
├── routes/          # Route configs + ProtectedRoute
├── App.jsx          # Routes + Providers
└── main.jsx         # Entry + Providers (Query, Auth, etc.)

Q22: How to deploy a React app?

Answer:
Option 1: Static Hosting (Vercel/Netlify)

  • Build: npm run build

  • Drag dist/ folder → Done. Free, HTTPS, CDN.

Option 2: Docker + VM (AWS/GCP)

  • Dockerfile → multi-stage build → Nginx server.

  • Crucial: try_files $uri $uri/ /index.html; for client-side routing.

Env Variables:

  • .envVITE_API_URL=https://...

  • Access via import.meta.env.VITE_API_URL


🧪 CHAPTER 7: TESTING & DEBUGGING

Q23: How to test React components?

Answer:
Use Vitest + React Testing Library:

npm install -D vitest @testing-library/react @testing-library/jest-dom jsdom

Example:

test('button calls onClick', () => {
  const mockFn = vi.fn();
  render(<Button onClick={mockFn}>Click</Button>);
  fireEvent.click(screen.getByText(/click/i));
  expect(mockFn).toHaveBeenCalledTimes(1);
});

✅ Test user flows, not implementation details.


Q24: What are React DevTools? How to use them?

Answer:
Browser extension to:

  • Inspect component tree, props, state, hooks.

  • Highlight updates.

  • Profile performance.

TanStack Query Devtools for inspecting query cache, mutations, etc.


🧠 CHAPTER 8: INTERVIEW DEEP DIVES

Q25: Explain Virtual DOM and Reconciliation.

Answer:

  • Virtual DOM: Lightweight copy of real DOM in memory.

  • Reconciliation: React compares new VDOM with old → computes minimal changes → updates real DOM.

Diffing Algorithm:

  • Compares elements by type + key.

  • Updates only changed nodes → efficient.


Q26: What is the “key” prop? Why is it important?

Answer:
key helps React identify which items changed, added, or removed in lists.

❌ Bad: index as key (breaks on reordering).
✅ Good: Unique ID from data (e.g., todo.id).

Example:

{todos.map(todo => (
  <TodoItem key={todo.id} todo={todo} />
))}

Q27: What are React Fragments? Why use them?

Answer:
Fragments let you group elements without adding extra DOM nodes.

// ❌ Invalid: Adjacent JSX elements
return (
  <h1>Title</h1>
  <p>Content</p>
);

// ✅ Valid
return (
  <>
    <h1>Title</h1>
    <p>Content</p>
  </>
);

Useful for avoiding wrapper divs that break CSS layouts.


Q28: Explain React’s render cycle and batching.

Answer:

  • Render: Component function executes → returns JSX.

  • Commit: React updates DOM.

Batching: React groups multiple setState calls into a single re-render for performance.

⚠️ In async functions (setTimeout, promises), batching doesn’t happen → use unstable_batchedUpdates or flushSync (advanced).


Q29: What is “Lifting State Up”?

Answer:
When multiple components need shared state, move state to their closest common ancestor.

Example:

  • <Parent> holds state.

  • Pass state + setter as props to <ChildA> and <ChildB>.

Later, replace with Context or state management library (Redux, Zustand) if prop drilling becomes messy.


Q30: What are Portals? Use cases?

Answer:
Portals render children outside DOM hierarchy (e.g., modals, tooltips).

ReactDOM.createPortal(child, containerDOMElement);

Example: Modal rendered at document.body to avoid z-index/overflow issues.


🚀 BONUS: ADVANCED INTERVIEW QUESTIONS

Q31: How does React.lazy() and Suspense work?

Answer:
React.lazy() enables code-splitting — load components dynamically.

const LazyComponent = React.lazy(() => import('./LazyComponent'));

<Suspense fallback={<Spinner />}>
  <LazyComponent />
</Suspense>

✅ Reduces initial bundle size → faster load.


Q32: What is Server Components (React 18+)? How is it different?

Answer:

  • Server Components: Rendered on server → zero bundle size → access DB directly.

  • Client Components: Interactive, use hooks, run in browser.

✅ Use Server Components for data-heavy, static parts (tables, lists).
✅ Use Client Components for interactivity (forms, buttons).


Q33: Explain useTransition and useDeferredValue.

Answer:

  • useTransition: Mark state updates as non-urgent → avoid blocking UI.

      const [isPending, startTransition] = useTransition();
      startTransition(() => setSearchQuery(input));
    
  • useDeferredValue: Defer re-rendering expensive child components.

      const deferredQuery = useDeferredValue(query);
      // Child re-renders only after urgent updates
    

Q34: What is Zustand? When to use over Context?

Answer:
Zustand is a minimal state management library.

✅ Use over Context when:

  • You need simpler API (no Providers, no useContext).

  • Avoiding re-render waterfall (Context re-renders all consumers).

  • Need middleware, persist, devtools.

Example:

const useStore = create((set) => ({
  count: 0,
  inc: () => set((state) => ({ count: state.count + 1 })),
}));

const count = useStore((state) => state.count);

Q35: How to optimize React performance?

Answer:

  1. Memoize: React.memo, useMemo, useCallback.

  2. Code-splitting: React.lazy + Suspense.

  3. Virtualize lists: react-window for large lists.

  4. Avoid inline functions/objects in render.

  5. Use production build.

  6. Profile with React DevTools.

  7. Debounce expensive operations (search, resize).


FINAL CHECKLIST: ARE YOU REACT-READY?

Skill
Build components with hooks✔️
Manage state immutably✔️
Handle side effects with useEffect✔️
Create custom hooks✔️
Share state with Context✔️
Navigate with React Router✔️
Optimize with useMemo/useCallback✔️
Fetch data with SWR/TanStack Query✔️
Implement JWT auth + protected routes✔️
Structure scalable projects✔️
Deploy to Vercel/Docker✔️
Write basic tests✔️
Debug with DevTools✔️