0% found this document useful (0 votes)
444 views66 pages

Complete React Documentation

The Complete React Documentation provides an extensive overview of React, a popular JavaScript library for building user interfaces. It covers essential topics such as components, props, state management, event handling, and best practices, along with installation methods and project structure. The documentation is designed to help developers understand and effectively use React's features and capabilities.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
444 views66 pages

Complete React Documentation

The Complete React Documentation provides an extensive overview of React, a popular JavaScript library for building user interfaces. It covers essential topics such as components, props, state management, event handling, and best practices, along with installation methods and project structure. The documentation is designed to help developers understand and effectively use React's features and capabilities.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 66

Complete React Documentation

Table of Contents
1. Introduction to React
2. Getting Started

3. JSX Fundamentals
4. Components

5. Props
6. State Management
7. Event Handling

8. Conditional Rendering
9. Lists and Keys

10. React Hooks


11. Component Lifecycle
12. Context API

13. Error Boundaries


14. Performance Optimization

15. Forms and Controlled Components


16. Routing

17. State Management Libraries


18. Testing
19. Best Practices

20. Advanced Patterns

Introduction to React
React is a JavaScript library for building user interfaces, particularly web applications. Created by
Facebook (now Meta) in 2013, React has become one of the most popular frontend frameworks due to
its component-based architecture, virtual DOM, and declarative programming style.

Key Features
Component-Based Architecture: React applications are built using reusable components that manage
their own state and can be composed to create complex UIs.
Virtual DOM: React creates a virtual representation of the DOM in memory, allowing for efficient
updates by comparing the virtual DOM with the actual DOM and updating only what has changed.

Declarative Programming: You describe what the UI should look like for any given state, and React
handles the how of updating the DOM.

Unidirectional Data Flow: Data flows down from parent to child components through props, making
applications more predictable and easier to debug.

Rich Ecosystem: React has a vast ecosystem of libraries, tools, and community support.

Getting Started

Prerequisites
Before working with React, you should have a solid understanding of:

HTML and CSS

JavaScript ES6+ features (arrow functions, destructuring, modules, etc.)


DOM manipulation basics

Asynchronous JavaScript (Promises, async/await)

Installation Methods

Create React App (Recommended for Beginners)

bash

npx create-react-app my-app


cd my-app
npm start

Vite (Modern, Fast Alternative)

bash

npm create vite@latest my-react-app -- --template react


cd my-react-app
npm install
npm run dev

Manual Setup with Webpack


For more control over your build configuration, you can set up React manually with Webpack, though
this requires more configuration.

Project Structure
A typical React project structure looks like this:

my-app/
├── public/
│ ├── index.html
│ └── favicon.ico
├── src/
│ ├── components/
│ ├── hooks/
│ ├── utils/
│ ├── App.js
│ ├── App.css
│ ├── index.js
│ └── index.css
├── package.json
└── README.md

JSX Fundamentals
JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code within
JavaScript. It's transpiled to regular JavaScript function calls.

Basic JSX Syntax

jsx

// JSX element
const element = <h1>Hello, World!</h1>;

// JSX with expressions


const name = 'John';
const greeting = <h1>Hello, {name}!</h1>;

// JSX with attributes


const link = <a href="https://example.com" target="_blank">Click me</a>;

JSX Rules and Conventions


Single Root Element: JSX expressions must have one parent element or use React Fragments.

jsx

// Wrong
return (
<h1>Title</h1>
<p>Content</p>
);

// Correct
return (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);

// Using Fragment
return (
<>
<h1>Title</h1>
<p>Content</p>
</>
);

camelCase for Attributes: HTML attributes in JSX use camelCase naming.

jsx

// HTML
<div class="container" tabindex="1"></div>

// JSX
<div className="container" tabIndex="1"></div>

Self-Closing Tags: All tags must be properly closed.

jsx

<img src="image.jpg" alt="Description" />


<input type="text" />
JavaScript Expressions: Use curly braces to embed JavaScript expressions.

jsx

const user = { name: 'Alice', age: 30 };


return (
<div>
<h1>Welcome, {user.name}</h1>
<p>You are {user.age} years old</p>
<p>Next year you'll be {user.age + 1}</p>
</div>
);

Components
Components are the building blocks of React applications. They are reusable pieces of UI that can have
their own state and logic.

Function Components
Function components are the modern way to create components in React. They are simpler and use
hooks for state and lifecycle management.

jsx

// Basic function component


function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}

// Arrow function component


const Welcome = (props) => {
return <h1>Hello, {props.name}</h1>;
};

// Implicit return for simple components


const Welcome = (props) => <h1>Hello, {props.name}</h1>;

Class Components (Legacy)


While function components are preferred, you may encounter class components in older codebases.

jsx
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}

Component Composition
Components can contain other components, allowing you to build complex UIs from simple building
blocks.

jsx
function App() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}

function Header() {
return (
<header>
<h1>My Website</h1>
<Navigation />
</header>
);
}

function Navigation() {
return (
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
);
}

Props
Props (properties) are how you pass data from parent components to child components. They are read-
only and help make components reusable.

Basic Props Usage

jsx
// Parent component
function App() {
return (
<div>
<UserCard name="Alice" age={30} email="[email protected]" />
<UserCard name="Bob" age={25} email="[email protected]" />
</div>
);
}

// Child component
function UserCard(props) {
return (
<div className="user-card">
<h2>{props.name}</h2>
<p>Age: {props.age}</p>
<p>Email: {props.email}</p>
</div>
);
}

Props Destructuring
You can destructure props for cleaner code:

jsx

function UserCard({ name, age, email }) {


return (
<div className="user-card">
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}

Default Props
You can provide default values for props:

jsx
function Button({ text, type = 'button', onClick }) {
return (
<button type={type} onClick={onClick}>
{text}
</button>
);
}

// Or using defaultProps (older method)


Button.defaultProps = {
type: 'button'
};

Prop Types (Optional)


For better development experience, you can use PropTypes to validate props:

jsx

import PropTypes from 'prop-types';

function UserCard({ name, age, email }) {


return (
<div className="user-card">
<h2>{name}</h2>
<p>Age: {age}</p>
<p>Email: {email}</p>
</div>
);
}

UserCard.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
email: PropTypes.string.isRequired
};

Children Prop
The special children prop allows you to pass elements between component tags:

jsx
function Card({ children, title }) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-content">
{children}
</div>
</div>
);
}

// Usage
function App() {
return (
<Card title="Welcome">
<p>This is the card content</p>
<button>Click me</button>
</Card>
);
}

State Management
State represents data that can change over time. In function components, we use the useState hook to
manage state.

useState Hook

jsx
import React, { useState } from 'react';

function Counter() {
const [count, setCount] = useState(0);

const increment = () => {


setCount(count + 1);
};

const decrement = () => {


setCount(count - 1);
};

return (
<div>
<h2>Count: {count}</h2>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
</div>
);
}

Multiple State Variables

jsx
function UserProfile() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);

return (
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<input
type="number"
value={age}
onChange={(e) => setAge(parseInt(e.target.value))}
placeholder="Age"
/>
</div>
);
}

State with Objects

jsx
function UserProfile() {
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});

const updateUser = (field, value) => {


setUser(prevUser => ({
...prevUser,
[field]: value
}));
};

return (
<div>
<input
value={user.name}
onChange={(e) => updateUser('name', e.target.value)}
placeholder="Name"
/>
<input
value={user.email}
onChange={(e) => updateUser('email', e.target.value)}
placeholder="Email"
/>
<input
type="number"
value={user.age}
onChange={(e) => updateUser('age', parseInt(e.target.value))}
placeholder="Age"
/>
</div>
);
}

State with Arrays

jsx
function TodoList() {
const [todos, setTodos] = useState([ ]);
const [inputValue, setInputValue] = useState('');

const addTodo = () => {


if (inputValue.trim()) {
setTodos([...todos, { id: Date.now(), text: inputValue, completed: false }]);
setInputValue('');
}
};

const toggleTodo = (id) => {


setTodos(todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
));
};

const deleteTodo = (id) => {


setTodos(todos.filter(todo => todo.id !== id));
};

return (
<div>
<input
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
placeholder="Add a todo"
/>
<button onClick={addTodo}>Add</button>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}

Event Handling
React uses SyntheticEvents, which are wrappers around native events that provide consistent behavior
across different browsers.

Basic Event Handling

jsx

function Button() {
const handleClick = (event) => {
console.log('Button clicked!', event);
};

return <button onClick={handleClick}>Click me</button>;


}

Common Event Handlers

jsx
function FormExample() {
const [formData, setFormData] = useState({
name: '',
email: ''
});

const handleInputChange = (event) => {


const { name, value } = event.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};

const handleSubmit = (event) => {


event.preventDefault();
console.log('Form submitted:', formData);
};

const handleKeyPress = (event) => {


if (event.key === 'Enter') {
console.log('Enter key pressed');
}
};

return (
<form onSubmit={handleSubmit}>
<input
name="name"
value={formData.name}
onChange={handleInputChange}
onKeyPress={handleKeyPress}
placeholder="Name"
/>
<input
name="email"
value={formData.email}
onChange={handleInputChange}
placeholder="Email"
/>
<button type="submit">Submit</button>
</form>
);
}

Passing Arguments to Event Handlers

jsx

function ItemList() {
const items = ['Apple', 'Banana', 'Cherry'];

const handleItemClick = (item, index) => {


console.log(`Clicked ${item} at index ${index}`);
};

return (
<ul>
{items.map((item, index) => (
<li key={index} onClick={() => handleItemClick(item, index)}>
{item}
</li>
))}
</ul>
);
}

Conditional Rendering
React allows you to conditionally render components or elements based on certain conditions.

Using if Statements

jsx

function UserGreeting({ user }) {


if (user) {
return <h1>Welcome back, {user.name}!</h1>;
}
return <h1>Please sign in.</h1>;
}

Using Ternary Operator

jsx
function UserGreeting({ user }) {
return (
<h1>
{user ? `Welcome back, ${user.name}!` : 'Please sign in.'}
</h1>
);
}

Using Logical && Operator

jsx

function Notification({ message, show }) {


return (
<div>
{show && (
<div className="notification">
{message}
</div>
)}
</div>
);
}

Switch-Case Pattern

jsx
function StatusMessage({ status }) {
const renderMessage = () => {
switch (status) {
case 'loading':
return <div>Loading...</div>;
case 'success':
return <div className="success">Success!</div>;
case 'error':
return <div className="error">Something went wrong.</div>;
default:
return null;
}
};

return renderMessage();
}

Lists and Keys


When rendering lists of elements, React requires each element to have a unique "key" prop for efficient
updates.

Basic List Rendering

jsx

function ShoppingList() {
const items = ['Milk', 'Bread', 'Eggs', 'Butter'];

return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}

List with Objects

jsx
function UserList() {
const users = [
{ id: 1, name: 'Alice', email: '[email protected]' },
{ id: 2, name: 'Bob', email: '[email protected]' },
{ id: 3, name: 'Charlie', email: '[email protected]' }
];

return (
<div>
{users.map(user => (
<div key={user.id} className="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
))}
</div>
);
}

Keys Best Practices


Use Stable IDs: Prefer using stable, unique identifiers over array indices when possible.

jsx

// Good
{items.map(item => <Item key={item.id} data={item} />)}

// Avoid if items can be reordered


{items.map((item, index) => <Item key={index} data={item} />)}

Keys Must Be Unique Among Siblings: Keys only need to be unique among sibling elements, not
globally.

React Hooks
Hooks are functions that let you "hook into" React features like state and lifecycle methods from
function components.

useState Hook
Already covered in the State Management section, useState allows you to add state to function
components.
useEffect Hook
useEffect lets you perform side effects in function components. It serves the same purpose as
componentDidMount, componentDidUpdate, and componentWillUnmount combined.

jsx

import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {


const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchUser = async () => {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
} catch (error) {
console.error('Error fetching user:', error);
} finally {
setLoading(false);
}
};

fetchUser();
}, [userId]); // Effect depends on userId

if (loading) return <div>Loading...</div>;


if (!user) return <div>User not found</div>;

return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}

useEffect Cleanup
jsx

function Timer() {
const [count, setCount] = useState(0);

useEffect(() => {
const interval = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);

// Cleanup function
return () => {
clearInterval(interval);
};
}, [ ]); // Empty dependency array means this effect runs once

return <div>Timer: {count}</div>;


}

useContext Hook
useContext allows you to consume context values without nesting.

jsx
import React, { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

function ThemeProvider({ children }) {


const [theme, setTheme] = useState('light');

return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}

function ThemedButton() {
const { theme, setTheme } = useContext(ThemeContext);

return (
<button
className={`btn-${theme}`}
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
>
Switch to {theme === 'light' ? 'dark' : 'light'} theme
</button>
);
}

useReducer Hook
useReducer is an alternative to useState for managing complex state logic.

jsx
import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {


switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return initialState;
default:
throw new Error(`Unknown action type: ${action.type}`);
}
}

function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);

return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
<button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</div>
);
}

Custom Hooks
Custom hooks are JavaScript functions that start with "use" and can call other hooks.

jsx
// Custom hook for local storage
function useLocalStorage(key, initialValue) {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});

const setValue = (value) => {


try {
setStoredValue(value);
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Error saving to localStorage:', error);
}
};

return [storedValue, setValue];


}

// Usage
function App() {
const [name, setName] = useLocalStorage('name', '');

return (
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Your name"
/>
);
}

useMemo and useCallback


These hooks help optimize performance by memoizing values and functions.

jsx
import React, { useState, useMemo, useCallback } from 'react';

function ExpensiveComponent({ items, filter }) {


const [count, setCount] = useState(0);

// Memoize expensive calculation


const expensiveValue = useMemo(() => {
console.log('Calculating expensive value...');
return items
.filter(item => item.includes(filter))
.reduce((sum, item) => sum + item.length, 0);
}, [items, filter]);

// Memoize callback function


const handleClick = useCallback(() => {
setCount(prev => prev + 1);
}, [ ]);

return (
<div>
<p>Expensive value: {expensiveValue}</p>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
}

Component Lifecycle
While function components don't have traditional lifecycle methods, useEffect can replicate their
behavior.

Lifecycle Equivalents with useEffect

jsx
function MyComponent() {
// ComponentDidMount equivalent
useEffect(() => {
console.log('Component mounted');
}, [ ]);

// ComponentDidUpdate equivalent
useEffect(() => {
console.log('Component updated');
});

// ComponentWillUnmount equivalent
useEffect(() => {
return () => {
console.log('Component will unmount');
};
}, [ ]);

// Conditional effect (like componentDidUpdate with condition)


useEffect(() => {
console.log('Specific prop changed');
}, [specificProp]);

return <div>My Component</div>;


}

Context API
The Context API provides a way to pass data through the component tree without having to pass props
down manually at every level.

Creating and Using Context

jsx
import React, { createContext, useContext, useState } from 'react';

// Create context
const UserContext = createContext();

// Provider component
function UserProvider({ children }) {
const [user, setUser] = useState(null);

const login = (userData) => {


setUser(userData);
};

const logout = () => {


setUser(null);
};

return (
<UserContext.Provider value={{ user, login, logout }}>
{children}
</UserContext.Provider>
);
}

// Hook to use context


function useUser() {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser must be used within UserProvider');
}
return context;
}

// Components using context


function LoginButton() {
const { user, login, logout } = useUser();

if (user) {
return (
<div>
<span>Welcome, {user.name}!</span>
<button onClick={logout}>Logout</button>
</div>
);
}

return (
<button onClick={() => login({ name: 'John Doe', id: 1 })}>
Login
</button>
);
}

function App() {
return (
<UserProvider>
<div>
<h1>My App</h1>
<LoginButton />
</div>
</UserProvider>
);
}

Error Boundaries
Error boundaries are React components that catch JavaScript errors anywhere in their child component
tree.

jsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}

static getDerivedStateFromError(error) {
return { hasError: true };
}

componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
});
}

render() {
if (this.state.hasError) {
return (
<div>
<h2>Something went wrong.</h2>
<details style={{ whiteSpace: 'pre-wrap' }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}

return this.props.children;
}
}

// Usage
function App() {
return (
<ErrorBoundary>
<Header />
<MainContent />
<Footer />
</ErrorBoundary>
);
}

Performance Optimization

React.memo
React.memo is a higher-order component that memoizes the result of a component.

jsx

const ExpensiveComponent = React.memo(function ExpensiveComponent({ data, onClick }) {


console.log('ExpensiveComponent rendered');

return (
<div>
<h3>{data.title}</h3>
<button onClick={onClick}>Click me</button>
</div>
);
});

// With custom comparison


const OptimizedComponent = React.memo(function OptimizedComponent({ user }) {
return <div>{user.name}</div>;
}, (prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id;
});

Lazy Loading and Suspense

jsx
import React, { Suspense, lazy } from 'react';

const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
return (
<div>
<h1>My App</h1>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}

Code Splitting by Routes

jsx

import React, { Suspense, lazy } from 'react';


import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./components/Home'));


const About = lazy(() => import('./components/About'));
const Contact = lazy(() => import('./components/Contact'));

function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
</Router>
);
}

Forms and Controlled Components


Controlled Components
In controlled components, form data is handled by React state.

jsx
function ContactForm() {
const [formData, setFormData] = useState({
name: '',
email: '',
message: '',
category: 'general',
newsletter: false
});

const handleChange = (e) => {


const { name, value, type, checked } = e.target;
setFormData(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};

const handleSubmit = (e) => {


e.preventDefault();
console.log('Form submitted:', formData);
// Handle form submission
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
placeholder="Your Name"
required
/>

<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Your Email"
required
/>
<textarea
name="message"
value={formData.message}
onChange={handleChange}
placeholder="Your Message"
rows="4"
required
/>

<select
name="category"
value={formData.category}
onChange={handleChange}
>
<option value="general">General</option>
<option value="support">Support</option>
<option value="billing">Billing</option>
</select>

<label>
<input
type="checkbox"
name="newsletter"
checked={formData.newsletter}
onChange={handleChange}
/>
Subscribe to newsletter
</label>

<button type="submit">Submit</button>
</form>
);
}

Form Validation

jsx
function ValidatedForm() {
const [formData, setFormData] = useState({
email: '',
password: '',
confirmPassword: ''
});

const [errors, setErrors] = useState({});

const validateForm = () => {


const newErrors = {};

if (!formData.email) {
newErrors.email = 'Email is required';
} else if (!/\S+@\S+\.\S+/.test(formData.email)) {
newErrors.email = 'Email is invalid';
}

if (!formData.password) {
newErrors.password = 'Password is required';
} else if (formData.password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
}

if (formData.password !== formData.confirmPassword) {


newErrors.confirmPassword = 'Passwords do not match';
}

setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};

const handleChange = (e) => {


const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));

// Clear error when user starts typing


if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: ''
}));
}
};

const handleSubmit = (e) => {


e.preventDefault();
if (validateForm()) {
console.log('Form is valid:', formData);
// Submit form
}
};

return (
<form onSubmit={handleSubmit}>
<div>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
</div>

<div>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
placeholder="Password"
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>

<div>
<input
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
placeholder="Confirm Password"
/>
{errors.confirmPassword && <span className="error">{errors.confirmPassword}</span>}
</div>

<button type="submit">Register</button>
</form>
);
}

Uncontrolled Components with useRef


Sometimes you may want to use uncontrolled components that manage their own state.

jsx
import React, { useRef } from 'react';

function UncontrolledForm() {
const nameRef = useRef();
const emailRef = useRef();
const fileRef = useRef();

const handleSubmit = (e) => {


e.preventDefault();
console.log('Name:', nameRef.current.value);
console.log('Email:', emailRef.current.value);
console.log('File:', fileRef.current.files[0]);
};

return (
<form onSubmit={handleSubmit}>
<input
ref={nameRef}
type="text"
placeholder="Name"
defaultValue="John Doe"
/>
<input
ref={emailRef}
type="email"
placeholder="Email"
/>
<input
ref={fileRef}
type="file"
/>
<button type="submit">Submit</button>
</form>
);
}

Routing
React Router is the standard library for routing in React applications.

Basic Setup

bash
npm install react-router-dom

jsx
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Link, Navigate } from 'react-router-dom';

function App() {
return (
<Router>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
<li><Link to="/users">Users</Link></li>
</ul>
</nav>

<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
<Route path="/users" element={<Users />} />
<Route path="/users/:id" element={<UserDetail />} />
<Route path="/admin/*" element={<AdminRoutes />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Router>
);
}

function Home() {
return <h2>Home Page</h2>;
}

function About() {
return <h2>About Page</h2>;
}

function Contact() {
return <h2>Contact Page</h2>;
}

Route Parameters and Query Strings

jsx
import { useParams, useSearchParams, useNavigate } from 'react-router-dom';

function UserDetail() {
const { id } = useParams();
const [searchParams, setSearchParams] = useSearchParams();
const navigate = useNavigate();

const tab = searchParams.get('tab') || 'profile';

const handleTabChange = (newTab) => {


setSearchParams({ tab: newTab });
};

const goBack = () => {


navigate(-1); // Go back one page
};

const goToUsers = () => {


navigate('/users');
};

return (
<div>
<h2>User Detail - ID: {id}</h2>
<button onClick={goBack}>Go Back</button>
<button onClick={goToUsers}>Go to Users</button>

<div>
<button
className={tab === 'profile' ? 'active' : ''}
onClick={() => handleTabChange('profile')}
>
Profile
</button>
<button
className={tab === 'settings' ? 'active' : ''}
onClick={() => handleTabChange('settings')}
>
Settings
</button>
</div>

{tab === 'profile' && <div>Profile Content</div>}


{tab === 'settings' && <div>Settings Content</div>}
</div>
);
}

Protected Routes

jsx

function ProtectedRoute({ children }) {


const { user } = useUser(); // Assuming you have user context

if (!user) {
return <Navigate to="/login" replace />;
}

return children;
}

function App() {
return (
<Router>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
} />
</Routes>
</Router>
);
}

Nested Routes

jsx
function AdminRoutes() {
return (
<div>
<h2>Admin Panel</h2>
<nav>
<Link to="/admin/users">Users</Link>
<Link to="/admin/products">Products</Link>
<Link to="/admin/orders">Orders</Link>
</nav>

<Routes>
<Route path="users" element={<AdminUsers />} />
<Route path="products" element={<AdminProducts />} />
<Route path="orders" element={<AdminOrders />} />
<Route index element={<AdminDashboard />} />
</Routes>
</div>
);
}

State Management Libraries

Redux Toolkit (Modern Redux)

bash

npm install @reduxjs/toolkit react-redux

jsx
// store/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';

export const fetchUser = createAsyncThunk(


'user/fetchUser',
async (userId) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
);

const userSlice = createSlice({


name: 'user',
initialState: {
data: null,
loading: false,
error: null
},
reducers: {
clearUser: (state) => {
state.data = null;
},
updateUser: (state, action) => {
state.data = { ...state.data, ...action.payload };
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
}
});
export const { clearUser, updateUser } = userSlice.actions;
export default userSlice.reducer;

// store/store.js
import { configureStore } from '@reduxjs/toolkit';
import userReducer from './userSlice';

export const store = configureStore({


reducer: {
user: userReducer
}
});

// App.js
import { Provider } from 'react-redux';
import { store } from './store/store';

function App() {
return (
<Provider store={store}>
<UserProfile />
</Provider>
);
}

// Component using Redux


import { useSelector, useDispatch } from 'react-redux';
import { fetchUser, clearUser } from './store/userSlice';

function UserProfile() {
const { data: user, loading, error } = useSelector(state => state.user);
const dispatch = useDispatch();

useEffect(() => {
dispatch(fetchUser(1));
}, [dispatch]);

if (loading) return <div>Loading...</div>;


if (error) return <div>Error: {error}</div>;
if (!user) return <div>No user found</div>;

return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<button onClick={() => dispatch(clearUser())}>
Clear User
</button>
</div>
);
}

Zustand (Lightweight Alternative)

bash

npm install zustand

jsx
import { create } from 'zustand';

const useUserStore = create((set, get) => ({


user: null,
loading: false,
error: null,

fetchUser: async (userId) => {


set({ loading: true, error: null });
try {
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
set({ user, loading: false });
} catch (error) {
set({ error: error.message, loading: false });
}
},

updateUser: (updates) => {


set((state) => ({
user: { ...state.user, ...updates }
}));
},

clearUser: () => set({ user: null })


}));

// Component using Zustand


function UserProfile() {
const { user, loading, error, fetchUser, clearUser } = useUserStore();

useEffect(() => {
fetchUser(1);
}, [fetchUser]);

if (loading) return <div>Loading...</div>;


if (error) return <div>Error: {error}</div>;
if (!user) return <div>No user found</div>;

return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
<button onClick={clearUser}>Clear User</button>
</div>
);
}

Testing

Jest and React Testing Library

bash

npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event

jsx
// Button.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import Button from './Button';

describe('Button Component', () => {


test('renders button with text', () => {
render(<Button>Click me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
});

test('calls onClick when clicked', async () => {


const user = userEvent.setup();
const handleClick = jest.fn();

render(<Button onClick={handleClick}>Click me</Button>);


const button = screen.getByRole('button');

await user.click(button);
expect(handleClick).toHaveBeenCalledTimes(1);
});

test('is disabled when disabled prop is true', () => {


render(<Button disabled>Click me</Button>);
const button = screen.getByRole('button');
expect(button).toBeDisabled();
});
});

// Testing forms
// ContactForm.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ContactForm from './ContactForm';

test('submits form with correct data', async () => {


const user = userEvent.setup();
const mockSubmit = jest.fn();
render(<ContactForm onSubmit={mockSubmit} />);

await user.type(screen.getByLabelText(/name/i), 'John Doe');


await user.type(screen.getByLabelText(/email/i), '[email protected]');
await user.type(screen.getByLabelText(/message/i), 'Hello world');
await user.click(screen.getByRole('button', { name: /submit/i }));

await waitFor(() => {


expect(mockSubmit).toHaveBeenCalledWith({
name: 'John Doe',
email: '[email protected]',
message: 'Hello world'
});
});
});

// Testing hooks
// useCounter.test.js
import { renderHook, act } from '@testing-library/react';
import useCounter from './useCounter';

test('should increment counter', () => {


const { result } = renderHook(() => useCounter(0));

act(() => {
result.current.increment();
});

expect(result.current.count).toBe(1);
});

// Testing with context


// UserProfile.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import { UserContext } from './UserContext';
import UserProfile from './UserProfile';

const renderWithContext = (user) => {


return render(
<UserContext.Provider value={{ user }}>
<UserProfile />
</UserContext.Provider>
);
};

test('displays user name when logged in', () => {


const user = { name: 'John Doe', email: '[email protected]' };
renderWithContext(user);

expect(screen.getByText('Welcome, John Doe!')).toBeInTheDocument();


});

test('displays login prompt when not logged in', () => {


renderWithContext(null);

expect(screen.getByText('Please log in')).toBeInTheDocument();


});

Mocking API Calls

jsx
// ApiComponent.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import ApiComponent from './ApiComponent';

// Mock fetch
global.fetch = jest.fn();

beforeEach(() => {
fetch.mockClear();
});

test('displays loading state initially', () => {


fetch.mockResolvedValue({
ok: true,
json: async () => ({ name: 'John Doe' })
});

render(<ApiComponent userId={1} />);


expect(screen.getByText('Loading...')).toBeInTheDocument();
});

test('displays user data after successful fetch', async () => {


fetch.mockResolvedValue({
ok: true,
json: async () => ({ name: 'John Doe', email: '[email protected]' })
});

render(<ApiComponent userId={1} />);

await waitFor(() => {


expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('[email protected]')).toBeInTheDocument();
});
});

test('displays error message on fetch failure', async () => {


fetch.mockRejectedValue(new Error('API Error'));

render(<ApiComponent userId={1} />);

await waitFor(() => {


expect(screen.getByText(/error/i)).toBeInTheDocument();
});
});

Best Practices

Component Organization
Single Responsibility Principle: Each component should have one clear purpose.

jsx

// Bad - component doing too much


function UserDashboard({ userId }) {
const [user, setUser] = useState(null);
const [posts, setPosts] = useState([ ]);
const [notifications, setNotifications] = useState([ ]);
// ... lots of logic

return (
<div>
{/* Complex UI mixing different concerns */}
</div>
);
}

// Good - separated concerns


function UserDashboard({ userId }) {
return (
<div>
<UserProfile userId={userId} />
<UserPosts userId={userId} />
<UserNotifications userId={userId} />
</div>
);
}

File Organization:
src/
├── components/
│ ├── common/
│ │ ├── Button/
│ │ │ ├── Button.js
│ │ │ ├── Button.test.js
│ │ │ └── Button.css
│ │ └── Modal/
│ └── pages/
│ ├── Home/
│ └── UserProfile/
├── hooks/
├── context/
├── utils/
└── services/

Naming Conventions
Components: Use PascalCase

jsx

function UserProfile() { }
function ShoppingCart() { }

Variables and Functions: Use camelCase

jsx

const userName = 'John';


const handleSubmit = () => { };

Constants: Use UPPER_SNAKE_CASE

jsx

const API_BASE_URL = 'https://api.example.com';


const MAX_RETRY_ATTEMPTS = 3;

Boolean Props: Use is/has/can prefixes

jsx
<Button isLoading hasError canSubmit />

Props Patterns
Prop Spreading: Use with caution

jsx

// Good - explicit props


<Button text="Submit" type="primary" onClick={handleClick} />

// Okay - when you need to pass many props


<Button {...buttonProps} />

// Better - use composition


<Button>
<Icon name="submit" />
Submit
</Button>

Default Props: Provide sensible defaults

jsx

function Button({
children,
type = 'button',
variant = 'primary',
disabled = false,
...rest
}) {
return (
<button
type={type}
className={`btn btn-${variant}`}
disabled={disabled}
{...rest}
>
{children}
</button>
);
}
State Management Best Practices
Keep State Close: Don't lift state higher than necessary

jsx

// Bad - unnecessary global state


function App() {
const [searchTerm, setSearchTerm] = useState('');
return (
<div>
<SearchComponent searchTerm={searchTerm} setSearchTerm={setSearchTerm} />
<ResultsComponent searchTerm={searchTerm} />
</div>
);
}

// Good - state where it's needed


function SearchPage() {
const [searchTerm, setSearchTerm] = useState('');
return (
<div>
<SearchComponent searchTerm={searchTerm} setSearchTerm={setSearchTerm} />
<ResultsComponent searchTerm={searchTerm} />
</div>
);
}

Immutable Updates: Always update state immutably

jsx
// Bad
const addItem = (newItem) => {
items.push(newItem);
setItems(items);
};

// Good
const addItem = (newItem) => {
setItems(prevItems => [...prevItems, newItem]);
};

// For objects
const updateUser = (updates) => {
setUser(prevUser => ({ ...prevUser, ...updates }));
};

Performance Best Practices


Avoid Inline Objects and Functions: They cause unnecessary re-renders

jsx
// Bad
function MyComponent() {
return (
<ChildComponent
style={{ margin: 10 }}
onClick={() => console.log('clicked')}
/>
);
}

// Good
const styles = { margin: 10 };

function MyComponent() {
const handleClick = useCallback(() => {
console.log('clicked');
}, [ ]);

return (
<ChildComponent
style={styles}
onClick={handleClick}
/>
);
}

Use Keys Properly: Stable, unique keys for list items

jsx

// Bad
{items.map((item, index) => (
<Item key={index} data={item} />
))}

// Good
{items.map(item => (
<Item key={item.id} data={item} />
))}

Error Handling
Graceful Error Handling: Always handle potential errors
jsx

function UserProfile({ userId }) {


const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchUser = async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(`/api/users/${userId}`);

if (!response.ok) {
throw new Error(`Failed to fetch user: ${response.status}`);
}

const userData = await response.json();


setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};

fetchUser();
}, [userId]);

if (loading) return <LoadingSpinner />;


if (error) return <ErrorMessage error={error} />;
if (!user) return <div>User not found</div>;

return <div>{/* User profile UI */}</div>;


}

Advanced Patterns

Render Props Pattern

jsx
function DataFetcher({ url, children }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url);
const result = await response.json();
setData(result);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};

fetchData();
}, [url]);

return children({ data, loading, error });


}

// Usage
function App() {
return (
<DataFetcher url="/api/users">
{({ data, loading, error }) => {
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}}
</DataFetcher>
);
}

Higher-Order Components (HOCs)

jsx

function withAuth(WrappedComponent) {
return function AuthenticatedComponent(props) {
const { user } = useUser();

if (!user) {
return <div>Please log in to access this page.</div>;
}

return <WrappedComponent {...props} user={user} />;


};
}

// Usage
const ProtectedProfile = withAuth(UserProfile);

Compound Components Pattern

jsx
function Tabs({ children, defaultTab }) {
const [activeTab, setActiveTab] = useState(defaultTab);

return (
<div className="tabs">
{React.Children.map(children, child => {
return React.cloneElement(child, { activeTab, setActiveTab });
})}
</div>
);
}

function TabList({ children, activeTab, setActiveTab }) {


return (
<div className="tab-list">
{React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
isActive: activeTab === index,
onSelect: () => setActiveTab(index)
});
})}
</div>
);
}

function Tab({ children, isActive, onSelect }) {


return (
<button
className={`tab ${isActive ? 'active' : ''}`}
onClick={onSelect}
>
{children}
</button>
);
}

function TabPanels({ children, activeTab }) {


return (
<div className="tab-panels">
{React.Children.toArray(children)[activeTab]}
</div>
);
}
function TabPanel({ children }) {
return <div className="tab-panel">{children}</div>;
}

// Usage
function App() {
return (
<Tabs defaultTab={0}>
<TabList>
<Tab>Profile</Tab>
<Tab>Settings</Tab>
<Tab>Billing</Tab>
</TabList>
<TabPanels>
<TabPanel><div>Profile Content</div></TabPanel>
<TabPanel><div>Settings Content</div></TabPanel>
<TabPanel><div>Billing Content</div></TabPanel>
</TabPanels>
</Tabs>
);
}

Portal Pattern

jsx
import { createPortal } from 'react-dom';

function Modal({ children, isOpen, onClose }) {


if (!isOpen) return null;

return createPortal(
<div className="modal-overlay" onClick={onClose}>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<button className="modal-close" onClick={onClose}>×</button>
{children}
</div>
</div>,
document.body
);
}

// Usage
function App() {
const [showModal, setShowModal] = useState(false);

return (
<div>
<button onClick={() => setShowModal(true)}>Open Modal</button>
<Modal isOpen={showModal} onClose={() => setShowModal(false)}>
<h2>Modal Content</h2>
<p>This modal is rendered in a portal!</p>
</Modal>
</div>
);
}

Forward Refs Pattern

jsx
const FancyButton = React.forwardRef(function FancyButton(props, ref) {
return (
<button className="fancy-button" ref={ref} {...props}>
{props.children}
</button>
);
});

// Usage
function App() {
const buttonRef = useRef();

const focusButton = () => {


buttonRef.current?.focus();
};

return (
<div>
<FancyButton ref={buttonRef}>Click me</FancyButton>
<button onClick={focusButton}>Focus the fancy button</button>
</div>
);
}

This comprehensive React documentation covers all the essential concepts, patterns, and best practices
you need to build modern React applications. From basic components and state management to
advanced patterns and performance optimization, this guide provides practical examples and real-world
use cases that you can reference and build upon in your React projects.

You might also like