← Tutorials ☕ Java 🧩 DSA 🌿 Spring Boot 🚀 DevOps 🗂 MySQL 🏗 System Design ❓ FAQ 🌐 HTML 🔇 JavaScript ⚛ ReactJS 🌻 NodeJS 🐍 Python 🍏 MongoDB 📋 Cheatsheet
Tutorials › ReactJS

Introduction to ReactJS

Beginner · 5 min read

React is a JavaScript library for building user interfaces, developed by Meta. It uses a component-based architecture and a virtual DOM to efficiently update the browser. React is the most popular frontend library in the world.

Key Concepts

  • Components — reusable, self-contained UI pieces
  • JSX — HTML-like syntax inside JavaScript
  • Virtual DOM — React reconciles changes and batches DOM updates
  • Props — data passed from parent to child (read-only)
  • State — data that lives inside a component and causes re-renders
  • Hooks — functions that let you use state & lifecycle in function components

React Ecosystem

  • Vite — fast development build tool (preferred)
  • React Router — client-side routing
  • Redux Toolkit / Zustand — global state management
  • TanStack Query (React Query) — data fetching & caching
  • Tailwind CSS / CSS Modules — styling
  • Next.js — full-stack React framework with SSR

Project Setup with Vite

Beginner · 5 min read

# Create React app with Vite (fast, modern) npm create vite@latest my-app -- --template react cd my-app npm install npm run dev # starts dev server at http://localhost:5173 # Project structure my-app/ ├── src/ │ ├── main.jsx # entry point │ ├── App.jsx # root component │ ├── components/ │ └── assets/ ├── index.html ├── vite.config.js └── package.json
// src/main.jsx import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' import './index.css' ReactDOM.createRoot(document.getElementById('root')) .render(<App />)

JSX & Rendering

Beginner · 8 min read

JSX is a syntax extension that looks like HTML but compiles to React.createElement() calls. It is not HTML — there are important differences.

// JSX basics const element = <h1 className="title">Hello World</h1>; // Expressions in JSX — use curly braces {} const name = 'Aftab'; const el = <p>Hello, {name}! Today is {new Date().toDateString()}.</p>; // JSX differences from HTML // className instead of class <div className="container"></div> // htmlFor instead of for (on labels) <label htmlFor="email">Email</label> // Inline styles are objects <div style={{ color: 'red', fontSize: '16px' }}></div> // Must have a single root element (or Fragment) return ( <> {/* React.Fragment shorthand */} <h1>Title</h1> <p>Body</p> </> ); // Self-closing tags must close <img src="photo.jpg" alt="photo" /> <input type="text" /> <br />

Components & Props

Beginner · 10 min read

// Function component (modern React — preferred) function UserCard({ name, role, avatar }) { return ( <div className="card"> <img src={avatar} alt={name} /> <h2>{name}</h2> <p>{role}</p> </div> ); } // Using the component function App() { return ( <UserCard name="Aftab" role="Full Stack Dev" avatar="/images/aftab.jpg" /> ); } // Props with defaults function Button({ label, variant = 'primary', onClick, disabled = false }) { return ( <button className={`btn btn-${variant}`} onClick={onClick} disabled={disabled} > {label} </button> ); } // children prop function Card({ title, children }) { return ( <div className="card"> <h3>{title}</h3> <div>{children}</div> </div> ); } // Usage: <Card title="Profile"> <UserCard name="Aftab" /> </Card>

State — useState

Beginner · 10 min read

import { useState } from 'react' function Counter() { // [currentValue, setter] const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>+</button> <button onClick={() => setCount(count - 1)}>-</button> <button onClick={() => setCount(0)}>Reset</button> </div> ); } // Functional update (safe when new state depends on previous) setCount(prev => prev + 1); // Object state const [user, setUser] = useState({ name: '', email: '' }); // Always create a new object (don't mutate) setUser(prev => ({ ...prev, name: 'Aftab' })); // Array state const [items, setItems] = useState([]); setItems(prev => [...prev, newItem]); // add setItems(prev => prev.filter(i => i.id !== id)); // remove setItems(prev => prev.map(i => // update i.id === id ? { ...i, done: true } : i ));
⚠️ Never mutate state directly — always use the setter. React compares references; if you mutate and pass the same object, React won't re-render.

Event Handling

Beginner · 7 min read

function Form() { const [value, setValue] = useState(''); // e is SyntheticEvent — React's cross-browser wrapper const handleChange = (e) => setValue(e.target.value); const handleSubmit = (e) => { e.preventDefault(); // prevent page reload console.log('Submitted:', value); }; return ( <form onSubmit={handleSubmit}> <input value={value} onChange={handleChange} onKeyDown={(e) => e.key === 'Enter' && handleSubmit(e)} /> <button type="submit">Submit</button> </form> ); } // Passing extra args to handler <button onClick={(e) => handleDelete(item.id, e)}>Delete</button>

Conditional Rendering

Beginner · 7 min read

function StatusBanner({ isLoggedIn, isLoading, error }) { // 1. if/else early return if (isLoading) return <Spinner />; if (error) return <ErrorMessage msg={error.message} />; return ( <div> {/* 2. Ternary */} {isLoggedIn ? <p>Welcome back!</p> : <p>Please log in.</p> } {/* 3. Short-circuit && (render only if truthy) */} {isLoggedIn && <Dashboard />} {/* 4. Nullish coalescing */} {user?.name ?? 'Guest'} </div> ); }
⚠️ Don't use 0 && <Component /> — if the left side is 0 (falsy but not false), React renders the literal 0 on screen. Use count > 0 && ... instead.

Lists & Keys

Beginner · 7 min read

function ProductList({ products }) { return ( <ul> {products.map(product => ( <li key={product.id}> {/* key must be unique & stable */} <span>{product.name}</span> <span>₹{product.price}</span> </li> ))} </ul> ); } // With index (only OK if list never reorders or deletes) {items.map((item, index) => <li key={index}>{item}</li>)} // Nested lists {categories.map(cat => ( <div key={cat.id}> <h3>{cat.name}</h3> <ul> {cat.items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> </div> ))}

Forms & Controlled Inputs

Intermediate · 10 min read

function RegistrationForm() { const [form, setForm] = useState({ name: '', email: '', password: '', role: 'user' }); const [errors, setErrors] = useState({}); const handleChange = ({ target: { name, value } }) => setForm(prev => ({ ...prev, [name]: value })); const validate = () => { const e = {}; if (!form.name) e.name = 'Name required'; if (!form.email) e.email = 'Email required'; return e; }; const handleSubmit = (e) => { e.preventDefault(); const e2 = validate(); if (Object.keys(e2).length) { setErrors(e2); return; } submitToAPI(form); }; return ( <form onSubmit={handleSubmit}> <input name="name" value={form.name} onChange={handleChange} /> {errors.name && <span className="error">{errors.name}</span>} <input name="email" value={form.email} onChange={handleChange} /> {errors.email && <span className="error">{errors.email}</span>} <select name="role" value={form.role} onChange={handleChange}> <option value="user">User</option> <option value="admin">Admin</option> </select> <button type="submit">Register</button> </form> ); }

useEffect

Intermediate · 10 min read

useEffect runs side effects after rendering — data fetching, subscriptions, DOM manipulation, timers.

import { useState, useEffect } from 'react' function UserProfile({ userId }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { // runs after every render where userId changed let cancelled = false; // prevent stale updates const load = async () => { setLoading(true); const data = await fetchUser(userId); if (!cancelled) { setUser(data); setLoading(false); } }; load(); // Cleanup — runs before next effect or unmount return () => { cancelled = true; }; }, [userId]); // dependency array: re-run when userId changes if (loading) return <Spinner />; return <div>{user.name}</div>; } // Dependency array patterns useEffect(() => { ... }); // runs after EVERY render useEffect(() => { ... }, []); // runs ONCE after mount useEffect(() => { ... }, [id]); // runs when id changes // Example: subscribe/unsubscribe useEffect(() => { const sub = eventBus.subscribe('update', handleUpdate); return () => sub.unsubscribe(); }, []);

Context & useContext

Intermediate · 10 min read

Context solves prop drilling — passing props through many layers. It provides a way to share values across any component tree level.

import { createContext, useContext, useState } from 'react' // 1. Create context const ThemeContext = createContext({ theme: 'light', toggleTheme: () => {} }); // 2. Provider — wraps the component tree function ThemeProvider({ children }) { const [theme, setTheme] = useState('light'); const toggleTheme = () => setTheme(prev => prev === 'light' ? 'dark' : 'light'); return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); } // 3. Custom hook for clean usage function useTheme() { return useContext(ThemeContext); } // 4. Consumer — any nested component function NavBar() { const { theme, toggleTheme } = useTheme(); return ( <nav className={theme}> <button onClick={toggleTheme}>Toggle Theme</button> </nav> ); } // Root function App() { return ( <ThemeProvider> <NavBar /> <Main /> </ThemeProvider> ); }

useRef

Intermediate · 8 min read

useRef returns a mutable object whose .current value persists across renders without causing re-renders. Used for DOM access and storing mutable values.

import { useRef, useEffect } from 'react' // 1. DOM access function AutoFocusInput() { const inputRef = useRef(null); useEffect(() => { inputRef.current?.focus(); }, []); return <input ref={inputRef} placeholder="Auto-focused!" />; } // 2. Store mutable value without re-render function StopWatch() { const [time, setTime] = useState(0); const intervalRef = useRef(null); const start = () => { intervalRef.current = setInterval(() => setTime(t => t + 1), 1000); }; const stop = () => clearInterval(intervalRef.current); return ( <div> <p>{time}s</p> <button onClick={start}>Start</button> <button onClick={stop}>Stop</button> </div> ); } // 3. Store previous value function usePrevious(value) { const ref = useRef(); useEffect(() => { ref.current = value; }); return ref.current; }

useMemo & useCallback

Advanced · 10 min read

import { useMemo, useCallback } from 'react' // useMemo — memoize expensive computed value function ProductList({ products, search }) { const filtered = useMemo( () => products.filter(p => p.name.toLowerCase().includes(search.toLowerCase()) ), [products, search] // recompute only when these change ); return <ul>{filtered.map(p => <li key={p.id}>{p.name}</li>)}</ul>; } // useCallback — memoize function reference (for child props) function Parent() { const [count, setCount] = useState(0); // Without useCallback: new function on every render → Child always re-renders const handleClick = useCallback((id) => { deleteItem(id); }, []); // stable reference unless deps change return <Child onDelete={handleClick} />; } // React.memo — prevents re-render if props unchanged const Child = React.memo(function({ onDelete }) { console.log('Child rendered'); return <button onClick={() => onDelete(1)}>Delete</button>; });
💡 Don't over-optimize. Only add useMemo / useCallback when you have a proven performance problem. Premature memoization adds complexity without benefit.

Custom Hooks

Intermediate · 8 min read

Custom hooks extract reusable stateful logic from components. A custom hook is any function starting with use that calls other hooks.

// useFetch — reusable data-fetching hook function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; setLoading(true); setError(null); fetch(url) .then(r => r.json()) .then(d => !cancelled && (setData(d), setLoading(false))) .catch(e => !cancelled && (setError(e), setLoading(false))); return () => { cancelled = true; }; }, [url]); return { data, loading, error }; } // Usage function Users() { const { data, loading, error } = useFetch('/api/users'); if (loading) return <Spinner />; if (error) return <p>Error: {error.message}</p>; return <UserList users={data} />; } // useLocalStorage — persisted state function useLocalStorage(key, initial) { const [value, setValue] = useState( () => JSON.parse(localStorage.getItem(key)) ?? initial ); const set = (v) => { setValue(v); localStorage.setItem(key, JSON.stringify(v)); }; return [value, set]; } // useDebounce function useDebounce(value, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { const t = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(t); }, [value, delay]); return debounced; }

React Router v6

Intermediate · 10 min read

npm install react-router-dom // main.jsx import { BrowserRouter } from 'react-router-dom' ReactDOM.createRoot(root).render(<BrowserRouter><App/></BrowserRouter>) // App.jsx — route definitions import { Routes, Route, Navigate } from 'react-router-dom' function App() { return ( <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/users/:id" element={<UserDetail />} /> <Route path="/dashboard" element={ isLoggedIn ? <Dashboard/> : <Navigate to="/login" replace/> } /> <Route path="*" element={<NotFound />} /> </Routes> ); } // Navigation hooks import { useNavigate, useParams, useSearchParams, useLocation } from 'react-router-dom' const navigate = useNavigate(); navigate('/dashboard'); navigate(-1); // go back const { id } = useParams(); // /users/:id → id='42' const [params, setParams] = useSearchParams(); params.get('page'); // ?page=2 // Link component (no page reload) import { Link, NavLink } from 'react-router-dom' <Link to="/about">About</Link> <NavLink to="/home" className={({isActive}) => isActive ? 'active' : ''}>Home</NavLink>

API Calls & Data Fetching

Intermediate · 10 min read

// Basic pattern with useEffect function PostList() { const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetch('/api/posts') .then(r => r.json()) .then(data => { setPosts(data); setLoading(false); }); }, []); return loading ? <Spinner/> : <List items={posts}/>; } // TanStack Query (React Query) — recommended for production npm install @tanstack/react-query import { useQuery, useMutation, QueryClient, QueryClientProvider } from '@tanstack/react-query' const qc = new QueryClient(); // Wrap app: <QueryClientProvider client={qc}>...</QueryClientProvider> // useQuery — fetch and cache function Users() { const { data, isLoading, error } = useQuery({ queryKey: ['users'], queryFn: () => fetch('/api/users').then(r => r.json()), staleTime: 1000 * 60 // 1 min cache }); if (isLoading) return <Spinner/>; return <UserList users={data}/>; } // useMutation — create/update/delete const mutation = useMutation({ mutationFn: (newUser) => fetch('/api/users', { method: 'POST', body: JSON.stringify(newUser) }), onSuccess: () => qc.invalidateQueries({ queryKey: ['users'] }) });

Redux Toolkit

Advanced · 12 min read

npm install @reduxjs/toolkit react-redux // store/cartSlice.js import { createSlice } from '@reduxjs/toolkit' const cartSlice = createSlice({ name: 'cart', initialState: { items: [], total: 0 }, reducers: { addItem: (state, action) => { state.items.push(action.payload); // Immer allows mutation! state.total += action.payload.price; }, removeItem: (state, action) => { const idx = state.items.findIndex(i => i.id === action.payload); if (idx !== -1) state.items.splice(idx, 1); }, clearCart: (state) => { state.items = []; state.total = 0; } } }); export const { addItem, removeItem, clearCart } = cartSlice.actions; export default cartSlice.reducer; // store/index.js import { configureStore } from '@reduxjs/toolkit' export const store = configureStore({ reducer: { cart: cartReducer } }); // main.jsx import { Provider } from 'react-redux' <Provider store={store}><App/></Provider> // Component — read & dispatch import { useSelector, useDispatch } from 'react-redux' function Cart() { const { items, total } = useSelector(state => state.cart); const dispatch = useDispatch(); return ( <div> <p>Items: {items.length} | Total: ₹{total}</p> <button onClick={() => dispatch(clearCart())}>Clear</button> </div> ); }

Component Patterns

Advanced · 10 min read

// 1. Compound Components function Tabs({ children, defaultTab }) { const [active, setActive] = useState(defaultTab); return <TabContext.Provider value={{ active, setActive }}>{children}</TabContext.Provider>; } Tabs.Tab = function({ id, label }) { /* ... */ }; Tabs.Panel = function({ id, children }) { /* ... */ }; // Usage: <Tabs defaultTab="a"> <Tabs.Tab id="a" label="Tab A"/> <Tabs.Panel id="a">Content A</Tabs.Panel> </Tabs> // 2. Render Props function MouseTracker({ render }) { const [pos, setPos] = useState({ x: 0, y: 0 }); return ( <div onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}> {render(pos)} </div> ); } <MouseTracker render={({x, y}) => <p>{x},{y}</p>} /> // 3. Higher-Order Component (HOC) function withAuth(Component) { return function AuthenticatedComponent(props) { const { isLoggedIn } = useAuth(); return isLoggedIn ? <Component {...props} /> : <Redirect to="/login"/>; }; } const ProtectedDashboard = withAuth(Dashboard);

Performance Optimization

Advanced · 10 min read

// 1. Code splitting with React.lazy const Dashboard = React.lazy(() => import('./Dashboard')); function App() { return ( <Suspense fallback={<Spinner/>}> <Dashboard/> </Suspense> ); } // 2. Virtualization for large lists npm install @tanstack/react-virtual // Or: react-window, react-virtualized // 3. Avoid unnecessary re-renders // - React.memo for pure components // - useCallback for event handlers passed as props // - useMemo for expensive computations // - Split state: keep fast-changing state near where it's used // 4. useTransition (React 18) — mark updates as non-urgent import { useTransition } from 'react' const [isPending, startTransition] = useTransition(); const handleSearch = (e) => { setQuery(e.target.value); // urgent: update input immediately startTransition(() => { setResults(filter(data, e.target.value)); // non-urgent }); }; // 5. useDeferredValue — like debounce but tied to render priority const deferredQuery = useDeferredValue(query);

Error Boundaries

Advanced · 7 min read

Error boundaries catch JavaScript errors anywhere in their child tree, log them, and display a fallback UI. They must be class components (hooks don't support this yet).

class ErrorBoundary extends React.Component { state = { hasError: false, error: null }; static getDerivedStateFromError(error) { return { hasError: true, error }; } componentDidCatch(error, info) { // Log to error tracking (Sentry, etc.) logError(error, info.componentStack); } render() { if (this.state.hasError) { return ( <div> <h2>Something went wrong</h2> <button onClick={() => this.setState({ hasError: false })}> Retry </button> </div> ); } return this.props.children; } } // Wrap risky components <ErrorBoundary> <UserDashboard/> </ErrorBoundary> // react-error-boundary library (simpler alternative) npm install react-error-boundary import { ErrorBoundary } from 'react-error-boundary' <ErrorBoundary FallbackComponent={ErrorFallback}> <App/> </ErrorBoundary>