The Ultimate React Guide

Search for a command to run...

No comments yet. Be the first to comment.
🌱 Chapter 1: What is Kubernetes? Why Should You Care? 🧩 Analogy:Imagine you’re managing a fleet of delivery drones. You don’t manually fly each one. You define: how many drones, what packages they carry, where to deliver, what to do if one crash...

Welcome to your definitive JavaScript journey — whether you’re building your first web app, leveling up your skills, or preparing to dive into React.js, this guide is crafted to take you from the ground up to confident, modern JavaScript proficiency....

Django is a popular Python web framework used for building web applications. It is known for its ease of use, scalability, and robust security features. If you're looking to get started with Django, this guide will walk you through the basic steps. S...

Django, a well-liked Python web framework, has gained popularity recently because of its ease of use, adaptability, and durability. As more professionals venture into Django projects, it is crucial to be aware of common mistakes that can hinder proje...

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.
✅ 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.
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
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.
// 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).
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.
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.
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.
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.
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!
// 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.
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>;
}
[] → 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.
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.
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.
// 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;
}
// main.jsx
import { AuthProvider } from './context/AuthContext';
ReactDOM.createRoot(document.getElementById('root')).render(
<AuthProvider>
<App />
</AuthProvider>
);
// 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).
Single Page Applications (SPAs) need client-side routing. Enter React Router v6+.
Install:
npm install react-router-dom
// 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>
);
}
<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.
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
useRefas a USB drive you plug into your component — stores data across renders without causing updates.
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.
✅ 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
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.
User submits email/password → POST to /api/login
Server validates → returns a JWT (JSON Web Token)
Client stores JWT (in localStorage or httpOnly cookie)
Every subsequent request → Attach JWT in Authorization: Bearer <token>
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.
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.
User lands on /login
Enters credentials → Clicks “Sign In”
On success → Save token + user → Redirect to /dashboard
On every page load → Check token validity → Set user in context
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).
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.
✅ 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
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.
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.
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)
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.
Your app works on your machine. Now let’s ship it.
If your React app is client-side rendered (CSR) and talks to a separate backend API — this is perfect.
Steps:
Build your app: npm run build
Drag-and-drop dist/ or build/ folder to Vercel/Netlify
Done. You get HTTPS, CDN, and global edge network.
✅ Zero config. Free tier available. Ideal for portfolios, dashboards, marketing sites.
Need to run your React app alongside a Node.js backend? Or want full infrastructure control? Dockerize it.
# 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;"]
server {
listen 80;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html; # ← Crucial for React Router
}
}
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.
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) orREACT_APP_(in CRA) to expose to client. Never put secrets here — only public config.
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 />}
</>
);
}
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.
✅ 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
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.
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.
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.
Answer:
Two ways:
Functional Component (Modern Standard):
function Welcome({ name }) { return <h1>Hello, {name}</h1>; }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.
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.
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
useStatehook:const [count, setCount] = useState(0);
Answer:
useStatelets 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.
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.
Answer:
useEffecthandles side effects (data fetching, subscriptions, DOM changes).Syntax:
useEffect(() => { // Side effect code }, [dependencies]); // Dependency array
[]→ Runs once after mount (likecomponentDidMount).
[a, b]→ Re-runs ifaorbchanges.No array → Runs after every render (usually a bug).
Example:
useEffect(() => { fetchUser(userId); }, [userId]); // Re-fetch if userId changes
Answer:
Custom hooks extract reusable logic (with state/effects) into functions. Must start withuse.Example:
useLocalStoragefunction 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);
Answer:
useRefgives 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} />;
Answer:
Context API shares global state (theme, user, language) without “prop drilling”.Steps:
Create Context:
createContext()Wrap app with
ProviderConsume with
useContextExample:
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>; }
Answer:
Create aProtectedRoutewrapper 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> } />
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();
Answer:
useMemomemoizes 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.
Answer:
useCallbackmemoizes 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.
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} />
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>
Answer:
useEffect + fetchworks but lacks:
Caching
Background refetching
Pagination
Loading/error states
Optimistic updates
✅ Use SWR or TanStack Query instead.
Answer:
Feature SWR TanStack Query Philosophy Simple, React-first Enterprise, feature-rich Caching ✅ Automatic ✅ Advanced DevTools ❌ ✅ Built-in Mutations Basic ( mutate)Advanced ( useMutation)Pagination ✅ ✅ Bundle Size Smaller Larger Learning Curve Gentle Steeper ✅ SWR for simple apps. TanStack Query for complex data needs.
Answer:
User logs in → POST
/login→ server returns JWT.Store JWT in
localStorageorhttpOnly cookie.Attach
Authorization: Bearer <token>to API requests.Validate token on server for protected routes.
Logout → remove token.
⚠️ Security: Prefer
httpOnly cookies(immune to XSS) overlocalStorage.
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.)
Answer:
Option 1: Static Hosting (Vercel/Netlify)
Build:
npm run buildDrag
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:
.env→VITE_API_URL=https://...Access via
import.meta.env.VITE_API_URL
Answer:
Use Vitest + React Testing Library:npm install -D vitest @testing-library/react @testing-library/jest-dom jsdomExample:
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.
Answer:
Browser extension to:
Inspect component tree, props, state, hooks.
Highlight updates.
Profile performance.
✅ TanStack Query Devtools for inspecting query cache, mutations, etc.
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.
Answer:
keyhelps React identify which items changed, added, or removed in lists.❌ Bad:
indexas key (breaks on reordering).
✅ Good: Unique ID from data (e.g.,todo.id).Example:
{todos.map(todo => ( <TodoItem key={todo.id} todo={todo} /> ))}
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.
Answer:
Render: Component function executes → returns JSX.
Commit: React updates DOM.
✅ Batching: React groups multiple
setStatecalls into a single re-render for performance.⚠️ In async functions (setTimeout, promises), batching doesn’t happen → use
unstable_batchedUpdatesorflushSync(advanced).
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.
Answer:
Portals render children outside DOM hierarchy (e.g., modals, tooltips).ReactDOM.createPortal(child, containerDOMElement);Example: Modal rendered at
document.bodyto avoid z-index/overflow issues.
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.
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).
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
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);
Answer:
Memoize:
React.memo,useMemo,useCallback.Code-splitting:
React.lazy+Suspense.Virtualize lists:
react-windowfor large lists.Avoid inline functions/objects in render.
Use production build.
Profile with React DevTools.
Debounce expensive operations (search, resize).
| 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 | ✔️ |