React
Comprehensive notes for fresher developers joining the team.
What is React?โ
React is an open-source JavaScript library developed by Meta (formerly Facebook). It is used to build fast, dynamic, and interactive user interfaces โ especially for Single Page Applications (SPAs), which load a single HTML page and dynamically update content without full page reloads.
React treats the UI as a collection of small, independent, reusable pieces called components, making development more organised and maintainable.
Why Use React?
| Feature | Description |
|---|---|
| Component-Based Architecture | UI is broken into small, reusable components โ modular and easier to maintain |
| Virtual DOM | React uses a virtual copy of the DOM to minimise expensive real DOM operations, resulting in faster rendering |
| Reusable Code | Components can be reused across different parts of the app, reducing duplication |
| One-Way Data Flow | Data flows from parent to child, making the app predictable and easier to debug |
| Strong Ecosystem | Huge community, rich third-party libraries, and strong industry adoption |
Learning Flowโ
| Phase | Topics |
|---|---|
| Phase 1 โ Fundamentals | JSX, Components, Props, State, Events, Conditional Rendering, Lists |
| Phase 2 โ Hooks | useState, useEffect, useContext, useRef, useMemo, useCallback, Custom Hooks |
| Phase 3 โ Component Design | Composition, Lifting State Up, Controlled vs Uncontrolled, Reusability |
| Phase 4 โ Routing | React Router, Navigation, Dynamic Routes, Protected Routes |
| Phase 5 โ State Management | Context API, Redux, Zustand |
| Phase 6 โ Real World | API Handling, Forms, Styling, Performance, Testing, Deployment |
Phase 1 โ Fundamentalsโ
1. JSX (JavaScript XML)โ
JSX is a syntax extension for JavaScript that lets you write HTML-like code directly inside your JavaScript files. It makes writing React components more intuitive. JSX is not valid JavaScript on its own โ it gets compiled to regular JavaScript by tools like Babel before running in the browser.
// Basic example
const element = <h1>Hello World</h1>;
// Embedding JavaScript expressions inside {}
const name = "John";
const element = <h1>Hello, {name}!</h1>;
// Output: Hello, John!
// JSX with logic
const age = 20;
const element = <p>{age >= 18 ? "Adult" : "Minor"}</p>;
Key Rules of JSX:
- Every JSX expression must return a single parent element. Wrap multiple elements in a
<div>or a React Fragment (<>...</>). - JavaScript expressions go inside
{}curly braces. - Use
classNameinstead ofclass(classis a reserved keyword in JavaScript). - Self-closing tags must end with
/>(e.g.,<input />,<img />). - JSX attributes use camelCase (e.g.,
onClick,onChange,htmlFor).
Practice: react.dev โ Writing Markup with JSX ยท react.dev โ JavaScript in JSX with Curly Braces
2. Componentsโ
A component is a self-contained, reusable piece of UI. Think of components as building blocks โ a webpage made up of a header, sidebar, and footer, where each is a separate React component.
Every React application has at least one root component (usually called App) which contains all other components.
Functional Component (Recommended โ Modern React):
A plain JavaScript function that returns JSX. This is the preferred way to write components today.
function Greeting() {
return <h1>Hello, Welcome!</h1>;
}
export default Greeting;
Class Component (Older Approach):
Before React Hooks (React 16.8), class components were the only way to manage state and lifecycle methods. You will still encounter these in older codebases.
import React from "react";
class Greeting extends React.Component {
render() {
return <h1>Hello, Welcome!</h1>;
}
}
export default Greeting;
Comparison:
| Feature | Functional Component | Class Component |
|---|---|---|
| Syntax | Simple function | ES6 Class |
| State Management | Using Hooks (useState) | Using this.state |
| Lifecycle Methods | Using useEffect | componentDidMount, etc. |
| Code Length | Shorter, cleaner | More verbose |
| Recommended | Yes | Mostly outdated |
Practice: react.dev โ Your First Component ยท react.dev โ Importing & Exporting Components
3. Props (Passing Data)โ
Props (short for properties) are how React passes data from a parent component to a child component. Props are read-only โ a child component cannot modify the props it receives.
// Parent Component
function App() {
return <Greeting name="John" age={25} />;
}
// Child Component
function Greeting(props) {
return <h1>Hello, {props.name}! You are {props.age} years old.</h1>;
}
Using Destructuring (cleaner syntax):
function Greeting({ name, age }) {
return <h1>Hello, {name}! You are {age} years old.</h1>;
}
Default Props:
function Greeting({ name = "Guest" }) {
return <h1>Hello, {name}!</h1>;
}
Key Points:
- Props flow in one direction: parent โ child.
- Props can be strings, numbers, arrays, objects, or even functions.
- A child should never mutate its props.
Practice: react.dev โ Passing Props to a Component ยท react.dev โ Passing JSX as Children
4. State (useState)โ
State is data that belongs to a component and can change over time. Unlike props (which come from outside), state is managed inside the component. When state changes, React automatically re-renders the component to reflect the updated data.
const [stateName, setStateName] = useState(initialValue);
Counter Example:
import { useState } from "react";
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>
);
}
State with Objects:
const [user, setUser] = useState({ name: "John", age: 25 });
// To update one field, always spread the previous state
setUser({ ...user, age: 26 });
Key Points:
useStatereturns an array:[current value, updater function].- Never modify state directly (avoid
count = count + 1). Always use the setter function. - State updates may be asynchronous โ React batches multiple updates for performance.
Practice: react.dev โ State: A Component's Memory ยท react.dev โ useState Reference
5. Event Handlingโ
Event handling in React is similar to HTML but with a few differences. React uses synthetic events โ a cross-browser wrapper around native browser events.
function ClickDemo() {
function handleClick() {
alert("Button was clicked!");
}
return <button onClick={handleClick}>Click Me</button>;
}
// Passing arguments to event handlers
function greet(name) {
alert(`Hello, ${name}`);
}
<button onClick={() => greet("John")}>Greet</button>
// Handling input change
function InputDemo() {
const [value, setValue] = useState("");
return (
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Type something"
/>
);
}
Common Events in React:
| Event | Description |
|---|---|
onClick | Mouse click |
onChange | Input field value change |
onSubmit | Form submission |
onMouseOver | Mouse hover |
onKeyDown | Key pressed |
Practice: react.dev โ Responding to Events ยท react.dev โ Adding Event Handlers
6. Conditional Renderingโ
Conditional rendering means showing different UI based on certain conditions โ just like an if/else block in regular JavaScript.
// Ternary operator
function UserStatus({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? <h1>Welcome Back!</h1> : <h1>Please Log In</h1>}
</div>
);
}
// && Short-circuit (renders only if condition is true)
function Notification({ hasMessage }) {
return (
<div>
{hasMessage && <p>You have a new message!</p>}
</div>
);
}
// if/else before return (for complex logic)
function Dashboard({ role }) {
if (role === "admin") {
return <AdminPanel />;
}
return <UserPanel />;
}
Practice: react.dev โ Conditional Rendering ยท react.dev โ Rendering Lists
7. Lists and Keysโ
React uses JavaScript's .map() method to render arrays of data as JSX elements.
const fruits = ["Apple", "Banana", "Mango"];
function FruitList() {
return (
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
);
}
Using Unique IDs as Keys (Recommended):
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
users.map((user) => <li key={user.id}>{user.name}</li>);
Why Are Keys Important?
Keys help React identify which items have changed, been added, or removed. Use unique keys (like database IDs) for better performance and to avoid rendering bugs. Avoid using array index as a key when the list order may change.
Practice: react.dev โ Rendering Lists ยท react.dev โ Keeping List Items in Order with key
Phase 2 โ Hooksโ
8. What are Hooks?โ
Hooks are special built-in functions introduced in React 16.8 that allow functional components to use React features like state, lifecycle methods, and context โ without needing class components.
Rules of Hooks:
- Only call hooks at the top level of a function โ never inside loops, conditions, or nested functions.
- Only call hooks inside React functional components or custom hooks.
Practice: react.dev โ Hooks Overview ยท react.dev โ Rules of Hooks
9. useState โ State Managementโ
The most commonly used hook. Allows you to declare state variables inside a functional component.
function Toggle() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>Toggle</button>
{isVisible && <p>This is visible!</p>}
</div>
);
}
Practice: react.dev โ useState Reference ยท react.dev โ Updating State Based on Previous State
10. useEffect โ Side Effectsโ
useEffect is used to perform side effects โ operations that happen outside the normal render flow, such as fetching data from an API, setting up subscriptions, or manually manipulating the DOM.
useEffect(() => {
// side effect code
return () => {
// cleanup (optional โ runs before the effect re-runs or on unmount)
};
}, [dependencies]);
When does it run?
// Runs after every render
useEffect(() => { ... });
// Runs only once on mount (like componentDidMount)
useEffect(() => { ... }, []);
// Runs when 'count' changes
useEffect(() => { ... }, [count]);
Data Fetching Example:
import { useState, useEffect } from "react";
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []); // empty array = fetch only once on mount
return (
<ul>
{users.map((u) => <li key={u.id}>{u.name}</li>)}
</ul>
);
}
Practice: react.dev โ useEffect Reference ยท react.dev โ Synchronizing with Effects
11. useContext โ Global State Basicsโ
useContext lets you consume data from a Context โ a way to share values globally across the component tree without passing props through every level (prop drilling).
import { createContext, useContext } from "react";
// 1. Create the context
const ThemeContext = createContext();
// 2. Provide the value (wrap your app or a section of it)
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
// 3. Consume the value in any child โ no matter how deeply nested
function Toolbar() {
const theme = useContext(ThemeContext);
return <p>Current Theme: {theme}</p>;
}
Use Cases: User authentication data, theme (light/dark), language preferences, shopping cart data.
Practice: react.dev โ useContext Reference ยท react.dev โ Passing Data Deeply with Context
12. useRef โ DOM Access & Persistent Valuesโ
useRef returns a mutable object { current: value } that persists across renders without triggering a re-render when changed. It has two main uses:
- Accessing DOM elements directly (e.g., focusing an input)
- Storing mutable values that shouldn't cause a re-render
import { useRef } from "react";
// DOM access โ focus an input programmatically
function FocusInput() {
const inputRef = useRef(null);
return (
<>
<input ref={inputRef} placeholder="Click button to focus" />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
</>
);
}
// Storing a value without re-render
const renderCount = useRef(0);
renderCount.current += 1;
console.log("Renders:", renderCount.current);
Practice: react.dev โ useRef Reference ยท react.dev โ Referencing Values with Refs
13. useMemo โ Performance Optimisationโ
useMemo caches the result of an expensive calculation so it doesn't recompute on every render โ only when its dependencies change.
import { useMemo, useState } from "react";
function ExpensiveList({ items, filter }) {
const filteredItems = useMemo(() => {
return items.filter((item) => item.includes(filter));
}, [items, filter]); // only recalculates when items or filter changes
return <ul>{filteredItems.map((i) => <li key={i}>{i}</li>)}</ul>;
}
When to use: Only for genuinely expensive computations (large data processing, complex filtering). Don't overuse it for simple operations.
Practice: react.dev โ useMemo Reference ยท react.dev โ Skipping Expensive Recalculations
14. useCallback โ Function Optimisationโ
useCallback caches a function definition so it is not recreated on every render. This is especially useful when passing callback functions to child components wrapped in React.memo.
import { useCallback, useState } from "react";
function Parent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log("Button clicked");
}, []); // function is only created once
return <Child onClick={handleClick} />;
}
Difference from useMemo:
| Hook | Returns | Use Case |
|---|---|---|
useMemo | Cached value | Expensive calculations |
useCallback | Cached function | Stable function references for child components |
Practice: react.dev โ useCallback Reference ยท react.dev โ Skipping Re-renders with useCallback
15. Custom Hooksโ
Custom hooks are reusable functions that encapsulate hook logic. They always start with the use prefix. This lets you extract and share stateful logic across components.
import { useState, useEffect } from "react";
// A reusable hook for fetching data
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
setData(data);
setLoading(false);
})
.catch((err) => setError(err));
}, [url]);
return { data, loading, error };
}
// Usage in any component
function App() {
const { data, loading, error } = useFetch("https://api.example.com/posts");
if (loading) return <p>Loading...</p>;
if (error) return <p>Error occurred</p>;
return <p>{data?.title}</p>;
}
Practice: react.dev โ Reusing Logic with Custom Hooks ยท react.dev โ Extracting Your Own Custom Hook
Phase 3 โ Component Designโ
16. Component Compositionโ
Component composition is the practice of building complex UI by combining smaller, focused components. This is React's preferred approach over class inheritance.
The children prop is special โ it represents whatever JSX is passed between the opening and closing tags of a component.
function Card({ title, children }) {
return (
<div className="card">
<h2>{title}</h2>
<div>{children}</div>
</div>
);
}
function App() {
return (
<Card title="User Profile">
<p>Name: John Doe</p>
<p>Email: john@example.com</p>
</Card>
);
}
Practice: react.dev โ Passing JSX as Children ยท react.dev โ Thinking in React
17. Lifting State Upโ
When multiple components need to share the same state, move the state to their closest common ancestor (parent). The parent then passes the state and updater function down as props.
function Parent() {
const [inputValue, setInputValue] = useState("");
return (
<div>
<InputField onChange={setInputValue} />
<DisplayValue value={inputValue} />
</div>
);
}
function InputField({ onChange }) {
return <input onChange={(e) => onChange(e.target.value)} />;
}
function DisplayValue({ value }) {
return <p>You typed: {value}</p>;
}
Practice: react.dev โ Sharing State Between Components ยท react.dev โ Lifting State Up by Example
18. Controlled vs Uncontrolled Componentsโ
Controlled Component โ React state controls the form input value:
function ControlledInput() {
const [value, setValue] = useState("");
return (
<input value={value} onChange={(e) => setValue(e.target.value)} />
);
}
Uncontrolled Component โ the DOM handles the value via ref:
function UncontrolledInput() {
const inputRef = useRef();
const handleSubmit = () => alert(inputRef.current.value);
return (
<>
<input ref={inputRef} />
<button onClick={handleSubmit}>Submit</button>
</>
);
}
| Aspect | Controlled | Uncontrolled |
|---|---|---|
| Data handled by | React State | DOM |
| Validation | Easy (on every change) | Harder |
| Recommended for | Most use cases | Simple, quick forms |
Practice: react.dev โ Controlled & Uncontrolled Inputs ยท react.dev โ Controlled and Uncontrolled Components
19. Prop Drilling vs Contextโ
Prop Drilling occurs when data must be passed through many component layers just to reach a deeply nested component, even though intermediate components don't need the data.
App (has user data)
โโโ Dashboard
โโโ Sidebar
โโโ UserAvatar โ needs user data
Every component in the chain must pass user as a prop, even if it doesn't use it. This makes the code messy and hard to maintain.
Solution โ Context API:
const UserContext = createContext();
function App() {
return (
<UserContext.Provider value={{ name: "John" }}>
<Dashboard />
</UserContext.Provider>
);
}
// UserAvatar can directly access user data โ no prop drilling needed
function UserAvatar() {
const user = useContext(UserContext);
return <img alt={user.name} />;
}
Practice: react.dev โ Passing Data Deeply with Context ยท react.dev โ Before You Use Context
20. Reusable Componentsโ
Reusable components accept props to render different content, avoiding code duplication.
function Alert({ type, message }) {
const colors = {
success: "green",
error: "red",
warning: "orange",
};
return (
<div style={{ color: colors[type] }}>
{message}
</div>
);
}
// Usage
<Alert type="success" message="Saved successfully!" />
<Alert type="error" message="Something went wrong." />
Practice: react.dev โ Your First Component ยท react.dev โ Thinking in React
Phase 4 โ Routingโ
21. What is Routing?โ
Routing means navigating between different views/pages in a React app without a full browser page reload. React uses client-side routing โ the browser URL changes, but only the required component updates, making navigation fast and smooth.
Installation:
npm install react-router-dom
22. React Router Basicsโ
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="*" element={<NotFound />} /> {/* catch-all for 404 */}
</Routes>
</BrowserRouter>
);
}
| Component | Role |
|---|---|
BrowserRouter | Wraps the app and enables routing |
Routes | Container for all route definitions |
Route | Maps a URL path to a component |
Practice: React Router โ Official Tutorial
23. Navigationโ
Using <Link> (Declarative โ No Page Reload):
import { Link } from "react-router-dom";
function Navbar() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
);
}
Using useNavigate (Programmatic Navigation):
Use this when you need to navigate after an action (e.g., after a successful login).
import { useNavigate } from "react-router-dom";
function LoginPage() {
const navigate = useNavigate();
function handleLogin() {
// After successful login...
navigate("/dashboard");
}
return <button onClick={handleLogin}>Login</button>;
}
Practice: React Router โ Tutorial: Navigation
24. Dynamic Routes & Route Paramsโ
Define a route with a :param placeholder, then read it with useParams.
// Route definition
<Route path="/product/:id" element={<ProductDetail />} />
// Reading the parameter inside the component
import { useParams } from "react-router-dom";
function ProductDetail() {
const { id } = useParams();
return <h1>Product ID: {id}</h1>;
}
// URL /product/42 โ renders "Product ID: 42"
25. Nested Routesโ
Nested routes let you create layouts where a parent component renders shared UI (like a sidebar) and child routes render into it using <Outlet />.
// Route definition
<Route path="/dashboard" element={<Dashboard />}>
<Route path="profile" element={<Profile />} />
<Route path="settings" element={<Settings />} />
</Route>
// Dashboard component โ Outlet renders the active child route
import { Outlet, Link } from "react-router-dom";
function Dashboard() {
return (
<div>
<nav>
<Link to="profile">Profile</Link>
<Link to="settings">Settings</Link>
</nav>
<Outlet /> {/* Child route renders here */}
</div>
);
}
Practice: React Router โ Tutorial: Nested Routes
26. Protected Routesโ
Protected routes block access to certain pages unless the user is authenticated.
import { Navigate } from "react-router-dom";
function ProtectedRoute({ children }) {
const isAuthenticated = Boolean(localStorage.getItem("token"));
if (!isAuthenticated) {
return <Navigate to="/login" replace />;
}
return children;
}
// Usage
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
Phase 5 โ State Managementโ
27. Why State Management?โ
As apps grow, state spread across many components becomes difficult to manage. Dedicated state management provides a single source of truth โ a central place to store and update application-wide data.
When to use what:
useStateโ local, component-level state- Context API โ small to medium apps, or specific global concerns (auth, theme)
- Redux / Zustand โ large, complex apps with frequent global state changes
Practice: react.dev โ Managing State ยท react.dev โ Choosing the State Structure
28. Context API โ Auth Exampleโ
// authContext.js
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 const useAuth = () => useContext(AuthContext);
Practice: react.dev โ Passing Data Deeply with Context ยท react.dev โ Scaling Up with Reducer and Context
29. Redux โ Overviewโ
Redux is a standalone state management library best suited for large, complex applications.
| Concept | Description |
|---|---|
| Store | The single centralised object holding all application state |
| Action | A plain JS object describing what happened (e.g., { type: 'INCREMENT' }) |
| Reducer | A pure function that takes state + action and returns new state |
| Dispatch | The function used to send actions to the store |
Data flow:
UI Event โ dispatch(action) โ Reducer โ New State โ UI Re-renders
Redux Toolkit (RTK) is the official recommended way to write Redux โ it eliminates boilerplate with createSlice and configureStore:
import { createSlice, configureStore } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; },
},
});
export const { increment, decrement } = counterSlice.actions;
const store = configureStore({
reducer: { counter: counterSlice.reducer },
});
30. Zustand โ Lightweight Alternativeโ
Zustand is a minimal, no-boilerplate state management library. A good middle ground between Context API and full Redux.
import { create } from "zustand";
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}));
function Counter() {
const { count, increment } = useStore();
return <button onClick={increment}>{count}</button>;
}
| Library | Description | Best For |
|---|---|---|
| Zustand | Minimal API, no boilerplate | Medium apps needing simple global state |
| Recoil | Atom-based model, fine-grained updates | Complex shared state |
| Redux | Battle-tested, powerful devtools | Large enterprise apps |
Phase 6 โ Real Worldโ
31. API Handlingโ
Using fetch:
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts/1")
.then((res) => {
if (!res.ok) throw new Error("Network error");
return res.json();
})
.then((data) => console.log(data))
.catch((err) => console.error(err));
}, []);
Using Axios (popular alternative โ cleaner syntax, automatic JSON parsing):
npm install axios
import axios from "axios";
useEffect(() => {
axios.get("https://jsonplaceholder.typicode.com/posts/1")
.then((res) => console.log(res.data))
.catch((err) => console.error(err));
}, []);
Fetch vs Axios:
| Feature | Fetch | Axios |
|---|---|---|
| JSON Parsing | Manual (.json()) | Automatic |
| Error Handling | Must check res.ok | Throws on 4xx/5xx |
| Request Cancellation | Needs AbortController | Built-in |
Async/Await with Loading & Error States (the pattern you'll use most):
import { useState, useEffect } from "react";
function Posts() {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchPosts() {
try {
const res = await fetch("https://jsonplaceholder.typicode.com/posts");
const data = await res.json();
setPosts(data);
} catch (err) {
setError("Failed to fetch posts.");
} finally {
setLoading(false);
}
}
fetchPosts();
}, []);
if (loading) return <p>Loading...</p>;
if (error) return <p>{error}</p>;
return (
<ul>
{posts.slice(0, 5).map((p) => <li key={p.id}>{p.title}</li>)}
</ul>
);
}
Practice: MDN โ Using the Fetch API ยท Axios โ Getting Started ยท JSONPlaceholder โ Free Fake API to practice with
32. Forms & Validationโ
Controlled Form with Manual Validation:
function RegistrationForm() {
const [form, setForm] = useState({ name: "", email: "" });
const [errors, setErrors] = useState({});
function validate() {
const newErrors = {};
if (!form.name) newErrors.name = "Name is required";
if (!form.email.includes("@")) newErrors.email = "Invalid email";
return newErrors;
}
function handleSubmit(e) {
e.preventDefault();
const validationErrors = validate();
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
} else {
console.log("Form submitted:", form);
}
}
return (
<form onSubmit={handleSubmit}>
<input
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
placeholder="Name"
/>
{errors.name && <span>{errors.name}</span>}
<input
value={form.email}
onChange={(e) => setForm({ ...form, email: e.target.value })}
placeholder="Email"
/>
{errors.email && <span>{errors.email}</span>}
<button type="submit">Register</button>
</form>
);
}
React Hook Form (recommended for real projects โ minimises re-renders):
npm install react-hook-form
import { useForm } from "react-hook-form";
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email", { required: "Email is required" })} />
{errors.email && <p>{errors.email.message}</p>}
<button type="submit">Login</button>
</form>
);
}
Practice: react.dev โ Reacting to Input with State ยท React Hook Form โ Get Started
33. Styling in Reactโ
| Method | How to Use | Best For |
|---|---|---|
| Plain CSS | import "./styles.css" | Simple, global styles |
| CSS Modules | import styles from "./styles.module.css" | Scoped styles, no conflicts |
| Styled Components | CSS-in-JS, styles tied to component | Dynamic styles, design systems |
| Tailwind CSS | Utility class-based styling | Rapid UI development |
Tailwind Example:
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Submit
</button>
Practice: Tailwind CSS โ Installation ยท Tailwind CSS โ Interactive Playground
34. Performance Optimisationโ
React.memo โ prevents a component from re-rendering when its parent re-renders, if its props haven't changed:
const ExpensiveComponent = React.memo(function ({ data }) {
return <p>{data}</p>;
});
Lazy Loading & Code Splitting โ load code only when needed, reducing initial bundle size:
import { Suspense, lazy } from "react";
const Dashboard = lazy(() => import("./pages/Dashboard"));
function App() {
return (
<Suspense fallback={<p>Loading Dashboard...</p>}>
<Dashboard />
</Suspense>
);
}
Practice: react.dev โ React.memo ยท react.dev โ lazy ยท react.dev โ Keeping Components Pure
35. Testingโ
Jest โ Unit Testing:
// sum.js
export function sum(a, b) { return a + b; }
// sum.test.js
import { sum } from "./sum";
test("adds 1 + 2 to equal 3", () => {
expect(sum(1, 2)).toBe(3);
});
React Testing Library โ test components from the user's perspective:
import { render, screen, fireEvent } from "@testing-library/react";
import Counter from "./Counter";
test("increments counter on button click", () => {
render(<Counter />);
const button = screen.getByText("Increment");
fireEvent.click(button);
expect(screen.getByText("Count: 1")).toBeInTheDocument();
});
Testing Philosophy: Test behaviour from the user's perspective โ not internal implementation details.
Practice: Jest โ Getting Started ยท React Testing Library โ Introduction
Advanced Conceptsโ
36. Virtual DOM & Reconciliationโ
React maintains a Virtual DOM โ a lightweight JavaScript representation of the real DOM. When state or props change:
- React creates a new Virtual DOM tree.
- It diffs the new tree against the previous one (this is called Reconciliation).
- Only the changed parts are updated in the real DOM.
This avoids costly full-page re-renders, making React apps fast.
Practice: react.dev โ Render and Commit ยท react.dev โ Preserving and Resetting State
37. Error Boundariesโ
Error Boundaries are class components that catch JavaScript errors in their child tree and display a fallback UI instead of crashing the whole app. There is currently no hooks-based equivalent โ you must use a class component for this.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error, info) {
console.error("Error caught:", error, info);
}
render() {
if (this.state.hasError) {
return <h2>Something went wrong. Please refresh.</h2>;
}
return this.props.children;
}
}
// Usage โ wrap any component you want to protect
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
Practice: react.dev โ Error Boundaries ยท react.dev โ Component lifecycle
38. Higher Order Components (HOC)โ
A HOC is a function that takes a component and returns a new component with added behaviour. Commonly used for auth guards, logging, and injecting data.
function withAuthGuard(Component) {
return function WrappedComponent(props) {
const isAuth = Boolean(localStorage.getItem("token"));
if (!isAuth) return <Navigate to="/login" />;
return <Component {...props} />;
};
}
const ProtectedDashboard = withAuthGuard(Dashboard);
Practice: react.dev โ Reusing Logic with Custom Hooks (modern alternative to HOCs) ยท React legacy docs โ Higher-Order Components
39. Portalsโ
Portals let you render a component's output into a different DOM node, outside the parent component's DOM hierarchy. Commonly used for modals, tooltips, and notifications.
import ReactDOM from "react-dom";
function Modal({ children }) {
return ReactDOM.createPortal(
<div className="modal">{children}</div>,
document.getElementById("modal-root") // a separate <div> in your index.html
);
}
Practice: react.dev โ createPortal ยท react.dev โ Rendering a Modal with a Portal
Ecosystem & Real-World Toolsโ
40. Next.jsโ
Next.js is a production-ready React framework that adds server-side rendering, file-based routing, API routes, and more โ without extra configuration.
| Feature | Benefit |
|---|---|
| Server-Side Rendering (SSR) | Renders on server โ better SEO, faster first load |
| Static Site Generation (SSG) | Pre-built pages โ ultra-fast performance |
| File-based Routing | No React Router needed |
| API Routes | Build backend API endpoints inside the same project |
Practice: Next.js โ Learn Course (free, interactive) ยท Next.js โ Documentation
41. Authentication โ JWT & OAuthโ
JWT (JSON Web Token):
- After login, the server sends a signed token.
- Client stores it (usually in
localStorageor a cookie). - Client sends the token with every API request in the
Authorizationheader.
OAuth:
- Allows users to authenticate using a third-party provider (Google, GitHub, etc.).
- Libraries like Auth0, NextAuth.js, or Firebase Auth simplify this.
Practice: jwt.io โ Introduction to JWTs ยท NextAuth.js โ Getting Started
42. Environment Variablesโ
Store sensitive configuration values like API URLs and secret keys in .env files โ never hardcode them in your source code.
# .env
REACT_APP_API_URL=https://api.example.com
REACT_APP_SECRET_KEY=mysecretkey
const apiUrl = process.env.REACT_APP_API_URL;
Variables must start with
REACT_APP_in Create React App. Never commit.envfiles to version control.
Practice: Create React App โ Environment Variables ยท Vite โ Env Variables (if using Vite)
43. Deploymentโ
Build for Production:
npm run build
This generates an optimised, minified build in the /build folder.
Popular Hosting Platforms:
| Platform | Notes |
|---|---|
| Vercel | Best for React & Next.js apps, free tier available |
| Netlify | Simple drag-and-drop or Git-based deployment |
| GitHub Pages | Free, good for static React apps |
| AWS / Azure | Enterprise-grade, more configuration required |
Practice: Vercel โ Deploy a React App ยท Netlify โ Get Started