Welcome to the most comprehensive React.js guide available in 2026. This extensive react js complete guide covers every essential topic from fundamentals to advanced concepts, providing detailed explanations, practical code examples, and best practices for each area of React development.
React.js has evolved significantly since its creation, and mastering it requires understanding not just the basics, but the entire ecosystem, architectural patterns, performance optimization techniques, and modern development workflows. This guide systematically covers all React topics including JSX, components, props, state, lifecycle methods, hooks, context API, routing, forms, Redux, testing, performance optimization, server-side rendering, and advanced patterns.
Whether you’re a complete beginner starting your React journey, an intermediate developer looking to fill knowledge gaps, or an advanced developer seeking to master complex patterns, this react js complete guide serves as your comprehensive reference. Each section provides theoretical understanding, practical implementation examples, common pitfalls to avoid, and actionable best practices.
By the end of this guide, you’ll have mastered every major aspect of React development and be equipped to build production-grade applications that scale efficiently, maintain clean architecture, and deliver exceptional user experiences across all devices and platforms.
1. Introduction to React.js – Foundation and Philosophy
React.js Definition: React is a declarative, component-based JavaScript library created by Facebook (Meta) for building user interfaces, particularly single-page applications, using a virtual DOM for efficient rendering and a unidirectional data flow architecture.
What Makes React Different?
React revolutionized frontend development by introducing a paradigm shift from imperative to declarative programming. Instead of manually manipulating the DOM and managing complex state transitions, React allows developers to describe what the UI should look like for any given state, and the framework handles the updates efficiently.
The core philosophy of React centers around three fundamental principles: component composition, unidirectional data flow, and the virtual DOM reconciliation algorithm. These principles work together to create a predictable, maintainable development experience that scales from simple landing pages to complex enterprise applications serving millions of users.
- Declarative Programming: Describe the UI state declaratively rather than imperatively updating DOM elements
- Component-Based Architecture: Build encapsulated, reusable components that manage their own state and logic
- Learn Once, Write Anywhere: Use the same knowledge for web (React), mobile (React Native), desktop (Electron), and VR (React 360)
- Virtual DOM: Efficient diffing algorithm minimizes expensive DOM operations by batching updates
- One-Way Data Flow: Data flows from parent to child components, making applications predictable and debuggable
- JSX Syntax: XML-like syntax extension that makes component structure intuitive and readable
React Ecosystem Overview
React is not just a library but an entire ecosystem of tools, libraries, and frameworks. Understanding this ecosystem is crucial for effective development. The core React library handles UI rendering, while companion libraries handle routing (React Router), state management (Redux, Zustand), form handling (React Hook Form), data fetching (TanStack Query), and styling (styled-components, Tailwind CSS).
Core React
Component rendering, JSX, hooks, virtual DOM, reconciliation
React DOM
Browser-specific rendering, hydration, portals, event handling
React Router
Client-side routing, navigation, dynamic routes, nested routes
State Management
Redux, Zustand, MobX, Recoil, Context API, local state
Build Tools
Vite, Webpack, Create React App, Parcel, esbuild
Testing Tools
Jest, React Testing Library, Cypress, Playwright, Vitest
Key Takeaway: Master JSX syntax including conditional rendering and list mapping. Refer to the official React JSX documentation for deeper examples and updates.
2. JSX – JavaScript XML Syntax
JSX Definition: JSX is a syntax extension for JavaScript that allows writing HTML-like markup directly in JavaScript files, which Babel or TypeScript compilers transform into React.createElement() function calls during the build process.
Understanding JSX Fundamentals
JSX looks like HTML but is actually JavaScript. Every JSX element is syntactic sugar for calling React.createElement(component, props, …children). While you can write React without JSX, it has become the standard because it makes component structure more readable and allows leveraging JavaScript’s full power within markup.
// JSX Syntax - What You Write
const element = (
<div className="container">
<h1>Hello, {name}!</h1>
<p>Welcome to React</p>
</div>
);
// Compiled JavaScript - What React Executes
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Hello, ', name, '!'),
React.createElement('p', null, 'Welcome to React')
);
JSX Rules and Syntax
- Single Root Element: JSX expressions must have one parent element (use React.Fragment or <> for invisible wrappers)
- CamelCase Attributes: Use className instead of class, htmlFor instead of for, onClick instead of onclick
- Self-Closing Tags: Elements without children must be self-closed: <img />, <input />, <br />
- JavaScript Expressions: Embed expressions in curly braces: {variable}, {2 + 2}, {functionCall()}
- Conditional Rendering: Use ternary operators, logical &&, or if statements outside JSX
- Comments: Use {/* comment */} for comments inside JSX
Advanced JSX Patterns
function AdvancedJSXExample({ items, isLoading, userRole }) {
// Conditional rendering patterns
if (isLoading) {
return <LoadingSpinner />;
}
return (
<>
{/* Fragment shorthand - no extra DOM node */}
{/* Conditional rendering with ternary */}
{userRole === 'admin' ? (
<AdminPanel />
) : (
<UserPanel />
)}
{/* Logical AND for conditional display */}
{items.length > 0 && (
<ItemList items={items} />
)}
{/* Mapping arrays to elements */}
<ul>
{items.map((item, index) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
{/* Spread attributes */}
<Component {...props} additionalProp="value" />
{/* Children as a function (render props) */}
<DataProvider>
{data => <Display data={data} />}
</DataProvider>
</>
);
}
Key Takeaway: Master JSX syntax including conditional rendering, list mapping, and expression embedding—these patterns form the foundation of every React component you’ll write. Learn more at our comprehensive JSX guide.
3. Components – Building Blocks of React
Components Definition: Components are independent, reusable pieces of UI that encapsulate their own structure, logic, and styling, functioning as JavaScript functions or classes that accept inputs (props) and return React elements describing what should appear on screen.
Functional vs Class Components
React originally provided two ways to create components: functional components (simple functions returning JSX) and class components (ES6 classes extending React.Component). Since React 16.8 introduced hooks, functional components can handle state and lifecycle methods, making them the preferred approach for modern React development.
| Aspect | Functional Components | Class Components |
|---|---|---|
| Syntax | Simple JavaScript functions | ES6 classes extending React.Component |
| State Management | useState, useReducer hooks | this.state and this.setState() |
| Lifecycle Methods | useEffect hook | componentDidMount, componentDidUpdate, etc. |
| Performance | Slightly better (no instance) | Good with optimization |
| Code Readability | More concise, easier to understand | More verbose, complex this binding |
| Modern React | Recommended approach | Legacy, still supported |
Functional Component Examples
// Simple Functional Component
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
// Component with Multiple Props
function UserCard({ user, onEdit, onDelete }) {
return (
<div className="user-card">
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.email}</p>
<button onClick={() => onEdit(user.id)}>Edit</button>
<button onClick={() => onDelete(user.id)}>Delete</button>
</div>
);
}
// Component with Default Props
function Button({ text = "Click me", variant = "primary", onClick }) {
return (
<button className={`btn btn-${variant}`} onClick={onClick}>
{text}
</button>
);
}
// Component with Children
function Card({ title, children }) {
return (
<div className="card">
<h3>{title}</h3>
<div className="card-content">
{children}
</div>
</div>
);
}
// Usage:
<Card title="User Profile">
<p>User information goes here</p>
<UserCard user={userData} />
</Card>
Class Component Example (Legacy)
// Class Component - Legacy Approach
import React, { Component } from 'react';
class Counter extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
// Bind methods to this
this.increment = this.increment.bind(this);
}
componentDidMount() {
console.log('Component mounted');
}
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
console.log('Count updated:', this.state.count);
}
}
componentWillUnmount() {
console.log('Component will unmount');
}
increment() {
this.setState(prevState => ({
count: prevState.count + 1
}));
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
Key Takeaway: Focus on functional components for modern React development—they provide cleaner syntax, better performance, and full feature parity with class components through hooks. Explore advanced component patterns.
4. Props – Component Communication
Props Definition: Props (short for properties) are read-only data passed from parent components to child components, enabling component communication and data flow in a unidirectional manner from top to bottom in the component tree.
Understanding Props Flow
Props are React’s mechanism for passing data and functionality between components. They’re similar to function parameters—when a parent component renders a child component, it can pass data through props. Props are immutable from the child’s perspective, ensuring predictable data flow and making debugging easier.
// Parent Component - Passing Props
function App() {
const userData = {
name: "John Doe",
age: 30,
email: "john@example.com"
};
const handleClick = () => {
console.log("Button clicked in child component");
};
return (
<div>
{/* Passing different types of props */}
<UserProfile
user={userData} // Object prop
isAdmin={true} // Boolean prop
count={42} // Number prop
onButtonClick={handleClick} // Function prop
status="active" // String prop
/>
</div>
);
}
// Child Component - Receiving Props
function UserProfile({ user, isAdmin, count, onButtonClick, status }) {
return (
<div className="profile">
<h2>{user.name}</h2>
<p>Age: {user.age}</p>
<p>Email: {user.email}</p>
<p>Status: {status}</p>
{isAdmin && <span>Admin Badge</span>}
<p>Count: {count}</p>
<button onClick={onButtonClick}>Click Me</button>
</div>
);
}
Props Destructuring and Default Values
// Props Destructuring in Function Parameters
function ProductCard({ title, price, image, description = "No description available" }) {
return (
<div className="product">
<img src={image} alt={title} />
<h3>{title}</h3>
<p>${price}</p>
<p>{description}</p>
</div>
);
}
// Default Props (Alternative Method)
ProductCard.defaultProps = {
description: "No description available",
price: 0
};
// Props with TypeScript
interface ButtonProps {
text: string;
variant?: 'primary' | 'secondary' | 'danger';
onClick: () => void;
disabled?: boolean;
}
function Button({
text,
variant = 'primary',
onClick,
disabled = false
}: ButtonProps) {
return (
<button
className={`btn btn-${variant}`}
onClick={onClick}
disabled={disabled}
>
{text}
</button>
);
}
Special Props: children and Rest Props
// Children Prop - Composition Pattern
function Container({ children, className }) {
return (
<div className={`container ${className}`}>
{children}
</div>
);
}
// Usage:
<Container className="main">
<h1>Title</h1>
<p>Content</p>
</Container>
// Rest Props - Spreading Remaining Props
function Input({ label, error, ...restProps }) {
return (
<div className="input-group">
<label>{label}</label>
<input {...restProps} />
{error && <span className="error">{error}</span>}
</div>
);
}
// Usage - All HTML input attributes pass through
<Input
label="Email"
type="email"
placeholder="Enter email"
required
maxLength={50}
/>
Props Validation with PropTypes
import PropTypes from 'prop-types';
function Comment({ author, text, timestamp, likes }) {
return (
<div className="comment">
<h4>{author}</h4>
<p>{text}</p>
<small>{timestamp}</small>
<span>{likes} likes</span>
</div>
);
}
// PropTypes validation
Comment.propTypes = {
author: PropTypes.string.isRequired,
text: PropTypes.string.isRequired,
timestamp: PropTypes.instanceOf(Date),
likes: PropTypes.number
};
Comment.defaultProps = {
likes: 0,
timestamp: new Date()
};
Key Takeaway: Props enable component reusability and composition—design components to accept props that make them flexible while maintaining a clear, focused purpose for each component.
5. State Management – Component State
State Definition: State is mutable data that belongs to a component and triggers re-renders when updated, allowing components to be dynamic and interactive by responding to user actions, network responses, and other events.
useState Hook – Basic State Management
The useState hook is the fundamental building block for adding state to functional components. It returns an array with two elements: the current state value and a function to update it. When you call the setter function, React re-renders the component with the new state value.
import { useState } from 'react';
// Basic Counter Example
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>
);
}
// Multiple State Variables
function UserForm() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState('');
const [errors, setErrors] = useState({});
const handleSubmit = (e) => {
e.preventDefault();
// Validation logic
const newErrors = {};
if (!name) newErrors.name = 'Name is required';
if (!email) newErrors.email = 'Email is required';
if (!age || age < 18) newErrors.age = 'Must be 18 or older';
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
// Submit form
console.log({ name, email, age });
};
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
{errors.name && <span>{errors.name}</span>}
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
{errors.email && <span>{errors.email}</span>}
<input
type="number"
value={age}
onChange={(e) => setAge(e.target.value)}
placeholder="Age"
/>
{errors.age && <span>{errors.age}</span>}
<button type="submit">Submit</button>
</form>
);
}
State with Objects and Arrays
// State with Objects - Must Create New Object
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
preferences: {
theme: 'light',
notifications: true
}
});
const updateName = (newName) => {
setUser(prevUser => ({
...prevUser,
name: newName
}));
};
const updateTheme = (newTheme) => {
setUser(prevUser => ({
...prevUser,
preferences: {
...prevUser.preferences,
theme: newTheme
}
}));
};
return (
<div>
<input
value={user.name}
onChange={(e) => updateName(e.target.value)}
/>
<select
value={user.preferences.theme}
onChange={(e) => updateTheme(e.target.value)}
>
<option value="light">Light</option>
<option value="dark">Dark</option>
</select>
</div>
);
}
// State with Arrays - Must Create New Array
function TodoList() {
const [todos, setTodos] = useState([]);
const [inputValue, setInputValue] = useState('');
const addTodo = () => {
if (inputValue.trim()) {
setTodos(prevTodos => [
...prevTodos,
{ id: Date.now(), text: inputValue, completed: false }
]);
setInputValue('');
}
};
const toggleTodo = (id) => {
setTodos(prevTodos =>
prevTodos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const deleteTodo = (id) => {
setTodos(prevTodos => prevTodos.filter(todo => todo.id !== id));
};
return (
<div>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && addTodo()}
/>
<button onClick={addTodo}>Add Todo</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<span style={{
textDecoration: todo.completed ? 'line-through' : 'none'
}}>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
State Update Patterns
// Functional Updates - Use When New State Depends on Previous State
function Counter() {
const [count, setCount] = useState(0);
const incrementThreeTimes = () => {
// ❌ Wrong - Only increments once due to closure
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
// ✅ Correct - Uses functional update
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
};
return (
<button onClick={incrementThreeTimes}>
Count: {count}
</button>
);
}
// Lazy Initial State - Use for Expensive Computations
function ExpensiveComponent() {
// ❌ Runs on every render
const [state, setState] = useState(expensiveComputation());
// ✅ Runs only once on mount
const [state, setState] = useState(() => expensiveComputation());
return <div>{state}</div>;
}
Key Takeaway: Master useState for local component state—understand functional updates, immutability requirements, and when to use object vs. multiple state variables for optimal component design. Learn more about advanced state management patterns.
6. React Hooks – Complete Overview
Hooks Definition: Hooks are special functions that let functional components use React features like state, lifecycle methods, context, and more without writing class components, fundamentally changing how developers structure and share stateful logic.
useEffect – Side Effects and Lifecycle
The useEffect hook handles side effects in functional components, replacing componentDidMount, componentDidUpdate, and componentWillUnmount from class components. It runs after every render by default, but you can control when it runs using the dependency array.
import { useState, useEffect } from 'react';
// Basic useEffect - Runs After Every Render
function BasicExample() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
});
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
// useEffect with Dependency Array
function DataFetching({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
setLoading(true);
fetch(`https://api.example.com/users/${userId}`)
.then(response => response.json())
.then(data => {
if (!cancelled) {
setUser(data);
setLoading(false);
}
})
.catch(err => {
if (!cancelled) {
setError(err.message);
setLoading(false);
}
});
// Cleanup function
return () => {
cancelled = true;
};
}, [userId]); // Only re-run when userId changes
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <div>{user.name}</div>;
}
// Multiple useEffect Hooks - Separation of Concerns
function Dashboard() {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([]);
// Effect for user data
useEffect(() => {
fetchUser().then(setUser);
}, []);
// Effect for posts data
useEffect(() => {
if (user) {
fetchUserPosts(user.id).then(setPosts);
}
}, [user]);
// Effect for window resize
useEffect(() => {
const handleResize = () => {
console.log('Window resized:', window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return <div>Dashboard Content</div>;
}
useContext – Consuming Context
import { createContext, useContext, useState } from 'react';
// Create Context
const ThemeContext = createContext();
const UserContext = createContext();
// Provider Component
function App() {
const [theme, setTheme] = useState('light');
const [user, setUser] = useState(null);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<UserContext.Provider value={{ user, setUser }}>
<MainContent />
</UserContext.Provider>
</ThemeContext.Provider>
);
}
// Consuming Context with useContext
function MainContent() {
const { theme, setTheme } = useContext(ThemeContext);
const { user } = useContext(UserContext);
return (
<div className={`app theme-${theme}`}>
<h1>Welcome {user?.name || 'Guest'}</h1>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</div>
);
}
useReducer – Complex State Logic
import { useReducer } from 'react';
// Reducer function
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
return {
...state,
items: [...state.items, action.payload]
};
case 'REMOVE_ITEM':
return {
...state,
items: state.items.filter(item => item.id !== action.payload)
};
case 'UPDATE_QUANTITY':
return {
...state,
items: state.items.map(item =>
item.id === action.payload.id
? { ...item, quantity: action.payload.quantity }
: item
)
};
case 'CLEAR_CART':
return { ...state, items: [] };
default:
return state;
}
}
function ShoppingCart() {
const [state, dispatch] = useReducer(cartReducer, { items: [] });
const addItem = (product) => {
dispatch({ type: 'ADD_ITEM', payload: { ...product, quantity: 1 } });
};
const removeItem = (id) => {
dispatch({ type: 'REMOVE_ITEM', payload: id });
};
return (
<div>
<h2>Cart ({state.items.length} items)</h2>
{state.items.map(item => (
<div key={item.id}>
{item.name} - Qty: {item.quantity}
<button onClick={() => removeItem(item.id)}>Remove</button>
</div>
))}
</div>
);
}
useMemo and useCallback – Performance Optimization
import { useState, useMemo, useCallback } from 'react';
function ExpensiveComponent({ items, filter }) {
// Memoize expensive computation
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter(item => item.category === filter);
}, [items, filter]);
// Memoize callback function
const handleClick = useCallback((id) => {
console.log('Item clicked:', id);
}, []); // Empty array - function never changes
return (
<ul>
{filteredItems.map(item => (
<li key={item.id} onClick={() => handleClick(item.id)}>
{item.name}
</li>
))}
</ul>
);
}
useRef – Accessing DOM and Persistent Values
import { useRef, useEffect } from 'react';
function TextInputWithFocus() {
const inputRef = useRef(null);
const renderCount = useRef(0);
useEffect(() => {
// Focus input on mount
inputRef.current.focus();
// Track renders without causing re-renders
renderCount.current += 1;
});
return (
<div>
<input ref={inputRef} type="text" />
<p>Render count: {renderCount.current}</p>
</div>
);
}
// useRef for Interval
function Timer() {
const [time, setTime] = useState(0);
const intervalRef = useRef(null);
const startTimer = () => {
if (!intervalRef.current) {
intervalRef.current = setInterval(() => {
setTime(prev => prev + 1);
}, 1000);
}
};
const stopTimer = () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
useEffect(() => {
return () => stopTimer(); // Cleanup on unmount
}, []);
return (
<div>
<p>Time: {time}s</p>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</div>
);
}
Custom Hooks – Reusable Logic
// Custom Hook for Form Handling
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
};
const resetForm = () => {
setValues(initialValues);
setErrors({});
};
return { values, errors, setErrors, handleChange, resetForm };
}
// Using Custom Hook
function RegistrationForm() {
const { values, errors, setErrors, handleChange, resetForm } = useForm({
username: '',
email: '',
password: ''
});
const handleSubmit = (e) => {
e.preventDefault();
// Validation and submission logic
};
return (
<form onSubmit={handleSubmit}>
<input name="username" value={values.username} onChange={handleChange} />
<input name="email" value={values.email} onChange={handleChange} />
<input name="password" value={values.password} onChange={handleChange} type="password" />
<button type="submit">Register</button>
</form>
);
}
// Custom Hook for Data Fetching
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(url)
.then(res => res.json())
.then(data => {
if (!cancelled) {
setData(data);
setLoading(false);
}
})
.catch(err => {
if (!cancelled) {
setError(err);
setLoading(false);
}
});
return () => { cancelled = true; };
}, [url]);
return { data, loading, error };
}
// Using the Fetch Hook
function UserList() {
const { data: users, loading, error } = useFetch('/api/users');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
| Hook | Purpose | Common Use Cases |
|---|---|---|
| useState | Add state to functional components | Form inputs, toggles, counters, UI state |
| useEffect | Handle side effects and lifecycle | Data fetching, subscriptions, DOM manipulation |
| useContext | Consume context values | Themes, authentication, global settings |
| useReducer | Complex state logic with actions | Shopping carts, form wizards, state machines |
| useMemo | Memoize expensive computations | Filtering/sorting large lists, complex calculations |
| useCallback | Memoize callback functions | Event handlers passed to child components |
| useRef | Access DOM elements, persist values | Focus management, animations, timers |
Key Takeaway: Hooks enable powerful composition patterns—create custom hooks to encapsulate and reuse stateful logic across your application, making components cleaner and more maintainable. Explore advanced hook patterns.
7. Context API – Global State Management
Context API Definition: The Context API provides a way to share data across the component tree without manually passing props through every level, solving the “prop drilling” problem for global state like themes, authentication, and user preferences.
Creating and Using Context
import { createContext, useContext, useState } from 'react';
// 1. Create Context
const AuthContext = createContext(null);
// 2. Create Provider Component
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(false);
const login = async (email, password) => {
setLoading(true);
try {
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password })
});
const userData = await response.json();
setUser(userData);
localStorage.setItem('token', userData.token);
} catch (error) {
console.error('Login failed:', error);
} finally {
setLoading(false);
}
};
const logout = () => {
setUser(null);
localStorage.removeItem('token');
};
const value = {
user,
loading,
login,
logout,
isAuthenticated: !!user
};
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
// 3. Create Custom Hook for Easy Access
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
// 4. Wrap App with Provider
function App() {
return (
<AuthProvider>
<Navigation />
<MainContent />
</AuthProvider>
);
}
// 5. Use Context in Components
function Navigation() {
const { user, logout, isAuthenticated } = useAuth();
return (
<nav>
{isAuthenticated ? (
<>
<span>Welcome, {user.name}</span>
<button onClick={logout}>Logout</button>
</>
) : (
<Link to="/login">Login</Link>
)}
</nav>
);
}
Multiple Contexts and Composition
// Theme Context
const ThemeContext = createContext();
export 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>
);
}
// Settings Context
const SettingsContext = createContext();
export function SettingsProvider({ children }) {
const [settings, setSettings] = useState({
notifications: true,
language: 'en',
timezone: 'UTC'
});
const updateSettings = (newSettings) => {
setSettings(prev => ({ ...prev, ...newSettings }));
};
return (
<SettingsContext.Provider value={{ settings, updateSettings }}>
{children}
</SettingsContext.Provider>
);
}
// Compose Multiple Providers
function App() {
return (
<AuthProvider>
<ThemeProvider>
<SettingsProvider>
<AppContent />
</SettingsProvider>
</ThemeProvider>
</AuthProvider>
);
}
// Or Create a Combined Provider
function AppProviders({ children }) {
return (
<AuthProvider>
<ThemeProvider>
<SettingsProvider>
{children}
</SettingsProvider>
</ThemeProvider>
</AuthProvider>
);
}
Key Takeaway: Context API excels at sharing global state like themes and authentication—but avoid overusing it for all state management as it can cause performance issues with frequently updating values. See our complete Context API guide.
8. React Router – Navigation and Routing
React Router Definition: React Router is the standard routing library for React applications that enables declarative navigation, URL-based routing, nested routes, and dynamic route parameters while maintaining the single-page application experience.
Basic Routing Setup
import { BrowserRouter, Routes, Route, Link, Navigate } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/products">Products</Link>
<Link to="/contact">Contact</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/products" element={<Products />} />
<Route path="/contact" element={<Contact />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
Dynamic Routes and Parameters
import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
// Dynamic Route with Parameters
function App() {
return (
<Routes>
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/users/:userId/posts/:postId" element={<UserPost />} />
</Routes>
);
}
// Accessing Route Parameters
function ProductDetail() {
const { id } = useParams();
const navigate = useNavigate();
const [product, setProduct] = useState(null);
useEffect(() => {
fetch(`/api/products/${id}`)
.then(res => res.json())
.then(setProduct);
}, [id]);
if (!product) return <p>Loading...</p>;
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<button onClick={() => navigate('/products')}>Back to Products</button>
</div>
);
}
// Query Parameters
function SearchResults() {
const [searchParams, setSearchParams] = useSearchParams();
const query = searchParams.get('q');
const category = searchParams.get('category');
return (
<div>
<h2>Search Results for: {query}</h2>
<p>Category: {category}</p>
<button onClick={() => setSearchParams({ q: 'new query' })}>
New Search
</button>
</div>
);
}
Nested Routes and Layouts
// Nested Routes with Outlet
import { Outlet } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<Dashboard />}>
<Route index element={<DashboardHome />} />
<Route path="analytics" element={<Analytics />} />
<Route path="settings" element={<Settings />} />
</Route>
</Route>
</Routes>
);
}
// Layout Component
function Layout() {
return (
<div>
<header>Header Content</header>
<nav>Navigation</nav>
<main>
<Outlet /> {/* Child routes render here */}
</main>
<footer>Footer Content</footer>
</div>
);
}
Protected Routes and Authentication
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from './AuthContext';
// Protected Route Component
function ProtectedRoute({ children }) {
const { isAuthenticated, loading } = useAuth();
const location = useLocation();
if (loading) {
return <div>Loading...</div>;
}
if (!isAuthenticated) {
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children;
}
// Usage
function App() {
return (
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/public" element={<PublicPage />} />
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
<Route path="/admin" element={
<ProtectedRoute requiredRole="admin">
<AdminPanel />
</ProtectedRoute>
} />
</Routes>
);
}
Programmatic Navigation
import { useNavigate } from 'react-router-dom';
function LoginForm() {
const navigate = useNavigate();
const [credentials, setCredentials] = useState({ email: '', password: '' });
const handleSubmit = async (e) => {
e.preventDefault();
try {
await login(credentials);
// Navigate to dashboard after successful login
navigate('/dashboard', { replace: true });
} catch (error) {
console.error('Login failed:', error);
}
};
const goBack = () => {
navigate(-1); // Go back one step in history
};
const goHome = () => {
navigate('/', { state: { from: 'login' } }); // Pass state
};
return (
<form onSubmit={handleSubmit}>
{/* Form fields */}
<button type="submit">Login</button>
<button type="button" onClick={goBack}>Back</button>
</form>
);
}
Key Takeaway: React Router enables seamless navigation in single-page applications—leverage nested routes for complex layouts and implement protected routes early to secure authenticated areas. Check out advanced routing patterns.
9. Forms and Validation in React
Forms Definition: Forms in React require controlled components where form data is managed by React state, enabling validation, conditional rendering, and dynamic form behavior through JavaScript rather than traditional HTML form handling.
Controlled Components
import { useState } from 'react';
function ControlledForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: '',
country: '',
acceptTerms: false,
gender: ''
});
const [errors, setErrors] = useState({});
const [submitted, setSubmitted] = useState(false);
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
// Clear error when user starts typing
if (errors[name]) {
setErrors(prev => ({ ...prev, [name]: '' }));
}
};
const validateForm = () => {
const newErrors = {};
if (!formData.username || formData.username.length < 3) {
newErrors.username = 'Username must be at least 3 characters';
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(formData.email)) {
newErrors.email = 'Invalid email address';
}
if (formData.password.length < 8) {
newErrors.password = 'Password must be at least 8 characters';
}
if (!formData.country) {
newErrors.country = 'Please select a country';
}
if (!formData.acceptTerms) {
newErrors.acceptTerms = 'You must accept the terms';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validateForm()) {
console.log('Form submitted:', formData);
setSubmitted(true);
// API call here
}
};
if (submitted) {
return <div>Thank you for registering!</div>;
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>Username:</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
/>
{errors.username && <span className="error">{errors.username}</span>}
</div>
<div>
<label>Email:</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>
<div>
<label>Password:</label>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>
<div>
<label>Country:</label>
<select name="country" value={formData.country} onChange={handleChange}>
<option value="">Select Country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
</select>
{errors.country && <span className="error">{errors.country}</span>}
</div>
<div>
<label>
<input
type="radio"
name="gender"
value="male"
checked={formData.gender === 'male'}
onChange={handleChange}
/>
Male
</label>
<label>
<input
type="radio"
name="gender"
value="female"
checked={formData.gender === 'female'}
onChange={handleChange}
/>
Female
</label>
</div>
<div>
<label>
<input
type="checkbox"
name="acceptTerms"
checked={formData.acceptTerms}
onChange={handleChange}
/>
I accept the terms and conditions
</label>
{errors.acceptTerms && <span className="error">{errors.acceptTerms}</span>}
</div>
<button type="submit">Register</button>
</form>
);
}
React Hook Form – Performant Forms
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
// Validation Schema
const schema = yup.object({
username: yup.string().min(3).required('Username is required'),
email: yup.string().email('Invalid email').required('Email is required'),
password: yup.string().min(8).required('Password must be 8+ characters'),
age: yup.number().positive().integer().min(18).required()
}).required();
function ReactHookFormExample() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
reset
} = useForm({
resolver: yupResolver(schema)
});
const onSubmit = async (data) => {
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('Form data:', data);
reset();
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<input {...register('username')} placeholder="Username" />
{errors.username && <p>{errors.username.message}</p>}
</div>
<div>
<input {...register('email')} type="email" placeholder="Email" />
{errors.email && <p>{errors.email.message}</p>}
</div>
<div>
<input {...register('password')} type="password" placeholder="Password" />
{errors.password && <p>{errors.password.message}</p>}
</div>
<div>
<input {...register('age')} type="number" placeholder="Age" />
{errors.age && <p>{errors.age.message}</p>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
Key Takeaway: For simple forms, use controlled components; for complex forms with extensive validation, leverage React Hook Form for better performance and developer experience with less boilerplate code. See complete form handling guide.
10. Performance Optimization Techniques
Performance Optimization Definition: Performance optimization in React involves identifying and eliminating unnecessary re-renders, reducing bundle sizes, implementing code splitting, lazy loading, and leveraging memoization to ensure applications remain fast and responsive as they scale.
React.memo – Component Memoization
import { memo, useState } from 'react';
// Without memo - re-renders every time parent re-renders
function ExpensiveComponent({ data }) {
console.log('ExpensiveComponent rendered');
return <div>{data}</div>;
}
// With memo - only re-renders when props change
const MemoizedComponent = memo(function ExpensiveComponent({ data }) {
console.log('MemoizedComponent rendered');
return <div>{data}</div>;
});
// Custom comparison function
const CustomMemoized = memo(
function Component({ user }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
// Return true if passing nextProps would render same result
return prevProps.user.id === nextProps.user.id;
}
);
Code Splitting and Lazy Loading
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// Lazy load components
const Home = lazy(() => import('./pages/Home'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Profile = lazy(() => import('./pages/Profile'));
const Settings = lazy(() => import('./pages/Settings'));
// Loading component
function LoadingFallback() {
return (
<div className="loading-container">
<div className="spinner"></div>
<p>Loading...</p>
</div>
);
}
function App() {
return (
<BrowserRouter>
<Suspense fallback={<LoadingFallback />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
// Prefetching routes on hover
function Navigation() {
const prefetchRoute = (routeName) => {
import(`./pages/${routeName}`);
};
return (
<nav>
<Link
to="/dashboard"
onMouseEnter={() => prefetchRoute('Dashboard')}
>
Dashboard
</Link>
</nav>
);
}
Virtualization for Long Lists
import { FixedSizeList } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);
return (
<FixedSizeList
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}
// Before: Rendering 10,000 items - SLOW
function SlowList({ items }) {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}
// After: Only renders visible items - FAST
function FastList({ items }) {
return <VirtualizedList items={items} />;
}
| Technique | Use Case | Performance Gain |
|---|---|---|
| React.memo | Prevent re-renders of expensive components | High for pure components |
| useMemo | Cache expensive calculations | High for complex computations |
| useCallback | Prevent child re-renders from function props | Medium to High |
| Code Splitting | Reduce initial bundle size | Critical for large apps |
| Virtualization | Render large lists (1000+ items) | Critical for performance |
| Debouncing | Limit expensive operations (API calls, search) | High for user input |
| Production Build | Minification and optimization | Essential for deployment |
Key Takeaway: Always measure performance before optimizing—use React DevTools Profiler to identify actual bottlenecks rather than prematurely optimizing components that don’t impact user experience significantly. Read our detailed performance optimization guide.
🚀 Ready to Master React Development?
Join thousands of developers building production-ready React applications. Access in-depth tutorials, download starter templates, and get expert guidance from our experienced development team.
Start Learning Now Download Templates Get Expert HelpFrequently Asked Questions (FAQs)
FACT: React is a JavaScript library created by Facebook in 2013 that uses a component-based architecture and virtual DOM to build user interfaces efficiently.
React’s popularity stems from its simplicity, performance, and flexibility. The component-based architecture promotes code reuse and maintainability. The virtual DOM ensures efficient updates by minimizing expensive browser operations. React’s declarative approach makes code more predictable and easier to debug than traditional imperative programming. With React Native, developers can use their React skills for mobile development. The massive ecosystem, strong community support, and backing from Facebook ensure continuous improvement and long-term viability. Over 11 million websites use React, including Facebook, Instagram, Netflix, Airbnb, and WhatsApp, demonstrating its production-readiness at scale.
FACT: Since React 16.8 introduced hooks in 2019, functional components with hooks have become the recommended approach for all new React development, offering full feature parity with class components.
Focus primarily on functional components and hooks for modern React development. Hooks provide cleaner syntax, better code organization, easier testing, and more intuitive patterns for sharing stateful logic through custom hooks. While class components are still fully supported and not deprecated, the React team recommends hooks for new code. You should understand class component basics for maintaining legacy code, but invest your learning time in mastering hooks like useState, useEffect, useContext, useReducer, useMemo, and useCallback. The industry has largely adopted hooks as the standard approach.
FACT: The virtual DOM is a lightweight JavaScript representation of the actual DOM that React maintains in memory, enabling efficient updates through a reconciliation algorithm that calculates minimal changes needed.
When state changes in a React application, React creates a new virtual DOM tree and compares it with the previous version through a process called “diffing.” This comparison identifies exactly which parts of the actual DOM need updating. Instead of manipulating the real DOM directly (which is expensive), React batches these minimal changes and applies them efficiently in a single update. This approach dramatically improves performance because DOM manipulation is one of the slowest operations in web browsers. The virtual DOM also enables React’s declarative programming model where you describe what the UI should look like, and React handles the how.
FACT: Context API is built into React for sharing state across components without prop drilling, while Redux is an external library providing predictable state management with middleware support, time-travel debugging, and better performance for frequently updating state.
Use Context API for simple global state like themes, user authentication, language preferences, and other slow-changing data. Context works well for small to medium applications where state updates are infrequent. Choose Redux or Redux Toolkit for large applications with complex state logic, when you need middleware for async operations, when debugging benefits from time-travel capabilities, or when state updates frequently and causes performance issues with Context. Modern alternatives like Zustand offer Redux-like capabilities with less boilerplate. Many successful applications use both: Context API for slow-changing global state and Redux for complex application state.
FACT: React performance optimization focuses on preventing unnecessary re-renders through React.memo, useMemo, and useCallback, while code splitting and lazy loading reduce initial bundle sizes for faster load times.
Start by measuring performance with React DevTools Profiler to identify actual bottlenecks. Use React.memo to memoize expensive components that render frequently with the same props. Apply useMemo for expensive calculations and useCallback for callback functions passed to children. Implement code splitting with React.lazy and Suspense to load routes and large components on demand. For lists with hundreds or thousands of items, use virtualization libraries like react-window or react-virtualized. Avoid inline function definitions in render methods. Use production builds with minification. Implement debouncing for expensive operations like API calls. Always optimize based on real measurements rather than assumptions.
FACT: React hooks are special functions that let functional components use state, lifecycle methods, context, and other React features without writing class components, fundamentally simplifying component logic and enabling better code reuse.
Hooks solve several problems that existed with class components: complex lifecycle methods that mixed unrelated logic, difficulty reusing stateful logic between components, confusing this keyword binding, and verbose component definitions. With hooks, you can use state (useState), side effects (useEffect), context (useContext), and complex state logic (useReducer) in functional components. Custom hooks enable extracting and sharing stateful logic across components without higher-order components or render props. Hooks promote better code organization by grouping related logic together rather than splitting it across lifecycle methods. They offer better TypeScript support and make components easier to test and understand.
FACT: React forms use controlled components where form data is managed by React state, enabling real-time validation, conditional rendering, and dynamic form behavior through JavaScript rather than traditional HTML form handling.
For simple forms with few fields, use controlled components with useState to manage input values and basic validation logic. As forms grow complex, consider React Hook Form for better performance and developer experience with built-in validation support. React Hook Form minimizes re-renders by using uncontrolled components internally while providing a clean API. Combine it with validation libraries like Yup or Zod for schema-based validation. Always validate both client-side for immediate user feedback and server-side for security. Handle async validation for checking unique usernames or emails. Provide clear error messages and accessibility features. For multi-step forms, consider using a state machine library like XState to manage complex form flows.
FACT: Props are read-only data passed from parent to child components for one-way data flow, while state is mutable data owned by a component that triggers re-renders when updated through setState functions.
Props enable parent-child communication and component composition. They’re immutable from the child’s perspective, ensuring predictable data flow. Use props to pass data, configuration, and callback functions down the component tree. State represents data that changes over time within a component, such as form inputs, toggle states, fetched data, and UI state. When state updates, React re-renders the component and its children. Components can pass their state as props to children, creating a data flow from parent to child. Props come from outside the component and can’t be modified, while state is managed internally and updated through setter functions. Understanding this distinction is fundamental to React’s unidirectional data flow architecture.








No responses yet