Skip to main content

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?

FeatureDescription
Component-Based ArchitectureUI is broken into small, reusable components โ€” modular and easier to maintain
Virtual DOMReact uses a virtual copy of the DOM to minimise expensive real DOM operations, resulting in faster rendering
Reusable CodeComponents can be reused across different parts of the app, reducing duplication
One-Way Data FlowData flows from parent to child, making the app predictable and easier to debug
Strong EcosystemHuge community, rich third-party libraries, and strong industry adoption

Learning Flowโ€‹

PhaseTopics
Phase 1 โ€“ FundamentalsJSX, Components, Props, State, Events, Conditional Rendering, Lists
Phase 2 โ€“ HooksuseState, useEffect, useContext, useRef, useMemo, useCallback, Custom Hooks
Phase 3 โ€“ Component DesignComposition, Lifting State Up, Controlled vs Uncontrolled, Reusability
Phase 4 โ€“ RoutingReact Router, Navigation, Dynamic Routes, Protected Routes
Phase 5 โ€“ State ManagementContext API, Redux, Zustand
Phase 6 โ€“ Real WorldAPI 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 className instead of class (class is 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:

FeatureFunctional ComponentClass Component
SyntaxSimple functionES6 Class
State ManagementUsing Hooks (useState)Using this.state
Lifecycle MethodsUsing useEffectcomponentDidMount, etc.
Code LengthShorter, cleanerMore verbose
RecommendedYesMostly 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:

  • useState returns 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:

EventDescription
onClickMouse click
onChangeInput field value change
onSubmitForm submission
onMouseOverMouse hover
onKeyDownKey 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:

  1. Accessing DOM elements directly (e.g., focusing an input)
  2. 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:

HookReturnsUse Case
useMemoCached valueExpensive calculations
useCallbackCached functionStable 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>
</>
);
}
AspectControlledUncontrolled
Data handled byReact StateDOM
ValidationEasy (on every change)Harder
Recommended forMost use casesSimple, 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>
);
}
ComponentRole
BrowserRouterWraps the app and enables routing
RoutesContainer for all route definitions
RouteMaps 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.

ConceptDescription
StoreThe single centralised object holding all application state
ActionA plain JS object describing what happened (e.g., { type: 'INCREMENT' })
ReducerA pure function that takes state + action and returns new state
DispatchThe 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>;
}
LibraryDescriptionBest For
ZustandMinimal API, no boilerplateMedium apps needing simple global state
RecoilAtom-based model, fine-grained updatesComplex shared state
ReduxBattle-tested, powerful devtoolsLarge 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:

FeatureFetchAxios
JSON ParsingManual (.json())Automatic
Error HandlingMust check res.okThrows on 4xx/5xx
Request CancellationNeeds AbortControllerBuilt-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โ€‹

MethodHow to UseBest For
Plain CSSimport "./styles.css"Simple, global styles
CSS Modulesimport styles from "./styles.module.css"Scoped styles, no conflicts
Styled ComponentsCSS-in-JS, styles tied to componentDynamic styles, design systems
Tailwind CSSUtility class-based stylingRapid 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:

  1. React creates a new Virtual DOM tree.
  2. It diffs the new tree against the previous one (this is called Reconciliation).
  3. 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.

FeatureBenefit
Server-Side Rendering (SSR)Renders on server โ†’ better SEO, faster first load
Static Site Generation (SSG)Pre-built pages โ†’ ultra-fast performance
File-based RoutingNo React Router needed
API RoutesBuild 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 localStorage or a cookie).
  • Client sends the token with every API request in the Authorization header.

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 .env files 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:

PlatformNotes
VercelBest for React & Next.js apps, free tier available
NetlifySimple drag-and-drop or Git-based deployment
GitHub PagesFree, good for static React apps
AWS / AzureEnterprise-grade, more configuration required

Practice: Vercel โ€“ Deploy a React App ยท Netlify โ€“ Get Started