# React.js Complete Reference Guide
A comprehensive cheat sheet covering everything you need to know about React - from basic components to advanced hooks, with best practices and common patterns. Perfect for beginners and experienced developers.
---
## Table of Contents
1. [Getting Started](#getting-started)
2. [Components](#components)
3. [Props](#props)
4. [State Management](#state-management)
5. [Lifecycle Methods](#lifecycle-methods)
6. [Hooks](#hooks)
7. [Event Handling](#event-handling)
8. [Conditional Rendering](#conditional-rendering)
9. [Lists and Keys](#lists-and-keys)
10. [Forms](#forms)
11. [Context API](#context-api)
12. [Refs](#refs)
13. [Error Boundaries](#error-boundaries)
14. [Performance Optimization](#performance-optimization)
15. [Advanced Patterns](#advanced-patterns)
16. [Best Practices](#best-practices)
---
## Getting Started
### Installation
```bash
# Create new Next.js project (recommended)
npx create-next-app@latest my-app
# Or Create React App
npx create-react-app my-app
# Or with Vite (faster)
npm create vite@latest my-app -- --template react
# Navigate and install
cd my-app
npm install
npm start
```
### Basic Setup
```jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
function App() {
return <h1>Hello React!</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
```
---
## Components
### Functional Components (Modern)
```jsx
// Simple functional component
function Greeting() {
return <h1>Hello World!</h1>;
}
// With props
function Greeting({ name }) {
return <h1>Hello {name}!</h1>;
}
// Arrow function syntax
const Greeting = ({ name }) => {
return <h1>Hello {name}!</h1>;
};
// Implicit return (concise)
const Greeting = ({ name }) => <h1>Hello {name}!</h1>;
```
### Class Components (Legacy)
```jsx
import React, { Component } from 'react';
class Hello extends Component {
render() {
return (
<div className='message-box'>
Hello {this.props.name}
</div>
);
}
}
```
### Component Composition
```jsx
function Welcome() {
return (
<div>
<Header />
<MainContent />
<Footer />
</div>
);
}
```
### Fragments
```jsx
import React, { Fragment } from 'react';
// Using Fragment
function UserInfo() {
return (
<Fragment>
<UserAvatar />
<UserProfile />
</Fragment>
);
}
// Short syntax
function UserInfo() {
return (
<>
<UserAvatar />
<UserProfile />
</>
);
}
```
---
## Props
### Passing Props
```jsx
<Video
fullscreen={true}
autoplay={false}
title="My Video"
/>
```
### Receiving Props
```jsx
// Functional component
function Video({ fullscreen, autoplay, title }) {
return <div>{title}</div>;
}
// Class component
class Video extends Component {
render() {
const { fullscreen, autoplay, title } = this.props;
return <div>{title}</div>;
}
}
```
### Default Props
```jsx
// Functional component
function Button({ color = 'blue', size = 'medium' }) {
return <button className={`btn-${color} btn-${size}`}>Click</button>;
}
// Class component
class Button extends Component {
static defaultProps = {
color: 'blue',
size: 'medium'
};
render() {
return <button>Click</button>;
}
}
```
### Children Props
```jsx
function AlertBox({ children }) {
return (
<div className='alert-box'>
{children}
</div>
);
}
// Usage
<AlertBox>
<h1>Warning!</h1>
<p>You have pending notifications</p>
</AlertBox>
```
### Spread Props
```jsx
function VideoPlayer(props) {
return <VideoEmbed {...props} />;
}
```
---
## State Management
### useState Hook (Functional Components)
```jsx
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(0)}>
Reset
</button>
</div>
);
}
```
### Multiple State Variables
```jsx
function UserProfile() {
const [age, setAge] = useState(25);
const [name, setName] = useState('John');
const [todos, setTodos] = useState([]);
return <div>User: {name}, Age: {age}</div>;
}
```
### State with Objects
```jsx
function Form() {
const [formData, setFormData] = useState({
username: '',
email: '',
age: 0
});
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
return (
<form>
<input
name="username"
value={formData.username}
onChange={handleChange}
/>
</form>
);
}
```
### State with Arrays
```jsx
function TodoList() {
const [todos, setTodos] = useState(['Task 1', 'Task 2']);
// Add item
const addTodo = (newTodo) => {
setTodos([...todos, newTodo]);
};
// Remove item
const removeTodo = (index) => {
setTodos(todos.filter((_, i) => i !== index));
};
// Update item
const updateTodo = (index, newValue) => {
const newTodos = [...todos];
newTodos[index] = newValue;
setTodos(newTodos);
};
return <div>{/* Render todos */}</div>;
}
```
### Functional Updates
```jsx
function Counter() {
const [count, setCount] = useState(0);
// When new state depends on previous state
const increment = () => {
setCount(prevCount => prevCount + 1);
};
return <button onClick={increment}>Count: {count}</button>;
}
```
### Class Component State (Legacy)
```jsx
class Counter extends Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
// Or with class fields
state = { count: 0 };
increment = () => {
this.setState({ count: this.state.count + 1 });
// Or functional form
this.setState(prevState => ({
count: prevState.count + 1
}));
};
render() {
return (
<button onClick={this.increment}>
Count: {this.state.count}
</button>
);
}
}
```
---
## Lifecycle Methods
### Class Component Lifecycle
```jsx
class MyComponent extends Component {
// 1. Mounting
constructor(props) {
super(props);
this.state = { data: null };
}
// 2. After component mounts (DOM available)
componentDidMount() {
// Fetch data, add event listeners, timers
fetch('/api/data')
.then(res => res.json())
.then(data => this.setState({ data }));
}
// 3. Before component re-renders
shouldComponentUpdate(nextProps, nextState) {
// Return false to skip render (optimization)
return true;
}
// 4. After component updates
componentDidUpdate(prevProps, prevState) {
// Respond to prop/state changes
if (prevProps.userId !== this.props.userId) {
this.fetchUserData(this.props.userId);
}
}
// 5. Before component unmounts
componentWillUnmount() {
// Cleanup: remove event listeners, cancel timers
clearInterval(this.timerId);
}
// 6. Error handling
componentDidCatch(error, info) {
console.error('Error:', error);
this.setState({ hasError: true });
}
render() {
return <div>{this.state.data}</div>;
}
}
```
### Lifecycle Chart
| Phase | Method | Purpose |
|-------|--------|---------|
| **Mounting** | `constructor()` | Initialize state |
| | `render()` | Return JSX |
| | `componentDidMount()` | API calls, subscriptions |
| **Updating** | `shouldComponentUpdate()` | Performance optimization |
| | `render()` | Re-render component |
| | `componentDidUpdate()` | Post-update operations |
| **Unmounting** | `componentWillUnmount()` | Cleanup |
| **Error** | `componentDidCatch()` | Error handling |
---
## Hooks
### useState - State Management
```jsx
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const [items, setItems] = useState([]);
return <div>Count: {count}</div>;
}
```
### useEffect - Side Effects
```jsx
import { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
// Runs after every render
useEffect(() => {
document.title = `Count: ${count}`;
});
// Runs only once (on mount)
useEffect(() => {
console.log('Component mounted');
}, []);
// Runs when dependencies change
useEffect(() => {
console.log('Count changed:', count);
}, [count]);
// With cleanup
useEffect(() => {
const timer = setInterval(() => {
console.log('Tick');
}, 1000);
return () => {
clearInterval(timer); // Cleanup
};
}, []);
return <div>Count: {count}</div>;
}
```
### useEffect Patterns
```jsx
// Fetch data on mount
useEffect(() => {
async function fetchData() {
const response = await fetch('/api/data');
const data = await response.json();
setData(data);
}
fetchData();
}, []);
// Subscribe/Unsubscribe
useEffect(() => {
const subscription = DataSource.subscribe(handleChange);
return () => {
subscription.unsubscribe();
};
}, []);
// Event listeners
useEffect(() => {
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
```
### useContext - Consume Context
```jsx
import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{ background: theme === 'dark' ? '#333' : '#fff' }}
>
Current theme: {theme}
</button>
);
}
```
### useReducer - Complex State Logic
```jsx
import { useReducer } from 'react';
// Reducer function
function counterReducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return { count: 0 };
default:
throw new Error('Unknown action');
}
}
function Counter() {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
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>
);
}
```
### useRef - DOM References & Persistent Values
```jsx
import { useRef, useEffect } from 'react';
function TextInput() {
const inputRef = useRef(null);
const renderCount = useRef(0);
useEffect(() => {
// Focus input on mount
inputRef.current.focus();
// Track renders (doesn't cause re-render)
renderCount.current += 1;
});
return (
<div>
<input ref={inputRef} type="text" />
<p>Renders: {renderCount.current}</p>
</div>
);
}
```
### useMemo - Memoize Expensive Calculations
```jsx
import { useMemo, useState } from 'react';
function ExpensiveComponent({ items }) {
const [filter, setFilter] = useState('');
// Only recalculates when items or filter changes
const filteredItems = useMemo(() => {
console.log('Filtering items...');
return items.filter(item =>
item.name.toLowerCase().includes(filter.toLowerCase())
);
}, [items, filter]);
return (
<div>
<input
value={filter}
onChange={e => setFilter(e.target.value)}
/>
<ul>
{filteredItems.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
</div>
);
}
```
### useCallback - Memoize Functions
```jsx
import { useCallback, useState } from 'react';
function Parent() {
const [count, setCount] = useState(0);
const [otherState, setOtherState] = useState(0);
// Function is recreated only when count changes
const handleClick = useCallback(() => {
console.log('Count:', count);
}, [count]);
return (
<div>
<ChildComponent onClick={handleClick} />
<button onClick={() => setOtherState(otherState + 1)}>
Update Other State
</button>
</div>
);
}
```
### useLayoutEffect - Synchronous Effects
```jsx
import { useLayoutEffect, useRef, useState } from 'react';
function Tooltip() {
const ref = useRef(null);
const [position, setPosition] = useState({ top: 0, left: 0 });
// Fires synchronously before browser paint
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
setPosition({
top: rect.bottom,
left: rect.left
});
}, []);
return <div ref={ref}>Hover me</div>;
}
```
### useImperativeHandle - Customize Ref Exposure
```jsx
import { forwardRef, useImperativeHandle, useRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = '';
}
}));
return <input ref={inputRef} />;
});
// Usage
function Parent() {
const fancyInputRef = useRef();
return (
<>
<FancyInput ref={fancyInputRef} />
<button onClick={() => fancyInputRef.current.focus()}>
Focus Input
</button>
</>
);
}
```
### Custom Hooks
```jsx
// Custom hook for window size
function useWindowSize() {
const [size, setSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);
return size;
}
// Usage
function App() {
const { width, height } = useWindowSize();
return <div>Window: {width} x {height}</div>;
}
```
```jsx
// Custom hook for fetching data
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
setLoading(true);
const response = await fetch(url);
const json = await response.json();
setData(json);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
// Usage
function UserProfile({ userId }) {
const { data, loading, error } = useFetch(`/api/users/${userId}`);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>{data.name}</div>;
}
```
---
## Event Handling
### Handling Events
```jsx
function Button() {
const handleClick = (e) => {
e.preventDefault();
console.log('Button clicked!');
};
return <button onClick={handleClick}>Click Me</button>;
}
```
### Common Events
```jsx
function EventExamples() {
return (
<div>
{/* Click events */}
<button onClick={() => console.log('Clicked')}>Click</button>
<button onDoubleClick={() => console.log('Double clicked')}>
Double Click
</button>
{/* Form events */}
<input
onChange={(e) => console.log(e.target.value)}
onFocus={() => console.log('Focused')}
onBlur={() => console.log('Blurred')}
/>
{/* Keyboard events */}
<input
onKeyDown={(e) => console.log('Key down:', e.key)}
onKeyUp={(e) => console.log('Key up:', e.key)}
onKeyPress={(e) => console.log('Key press:', e.key)}
/>
{/* Mouse events */}
<div
onMouseEnter={() => console.log('Mouse entered')}
onMouseLeave={() => console.log('Mouse left')}
onMouseMove={(e) => console.log(e.clientX, e.clientY)}
>
Hover me
</div>
</div>
);
}
```
### Event with Parameters
```jsx
function TodoList() {
const [todos, setTodos] = useState(['Task 1', 'Task 2']);
const handleDelete = (index) => {
setTodos(todos.filter((_, i) => i !== index));
};
return (
<ul>
{todos.map((todo, index) => (
<li key={index}>
{todo}
<button onClick={() => handleDelete(index)}>Delete</button>
</li>
))}
</ul>
);
}
```
---
## Conditional Rendering
### Using If-Else
```jsx
function Greeting({ isLoggedIn }) {
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign in.</h1>;
}
```
### Ternary Operator
```jsx
function Greeting({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? <h1>Welcome back!</h1> : <h1>Please sign in.</h1>}
</div>
);
}
```
### Logical && Operator
```jsx
function Mailbox({ unreadMessages }) {
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 && (
<h2>You have {unreadMessages.length} unread messages.</h2>
)}
</div>
);
}
```
### Switch Statement
```jsx
function NotificationBadge({ type }) {
const renderBadge = () => {
switch(type) {
case 'success':
return <span className="badge-success">✓</span>;
case 'error':
return <span className="badge-error">✗</span>;
case 'warning':
return <span className="badge-warning">!</span>;
default:
return null;
}
};
return <div>{renderBadge()}</div>;
}
```
### Null Rendering
```jsx
function Warning({ showWarning }) {
if (!showWarning) {
return null;
}
return <div className="warning">Warning!</div>;
}
```
---
## Lists and Keys
### Rendering Lists
```jsx
function TodoList() {
const todos = [
{ id: 1, text: 'Learn React' },
{ id: 2, text: 'Build an app' },
{ id: 3, text: 'Deploy it' }
];
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
```
### Keys Best Practices
```jsx
// ✅ Good: Using unique IDs
{items.map(item => (
<div key={item.id}>{item.name}</div>
))}
// ❌ Bad: Using index as key (when order may change)
{items.map((item, index) => (
<div key={index}>{item.name}</div>
))}
// ✅ Acceptable: Using index when list is static
{staticItems.map((item, index) => (
<div key={index}>{item}</div>
))}
```
### Extracting Components with Keys
```jsx
function TodoItem({ todo }) {
return <li>{todo.text}</li>;
}
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
);
}
```
---
## Forms
### Controlled Components
```jsx
function LoginForm() {
const [formData, setFormData] = useState({
username: '',
password: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Submitted:', formData);
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
placeholder="Username"
/>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
);
}
```
### Form Elements
```jsx
function FormElements() {
const [text, setText] = useState('');
const [checked, setChecked] = useState(false);
const [selected, setSelected] = useState('option1');
return (
<form>
{/* Text input */}
<input
type="text"
value={text}
onChange={e => setText(e.target.value)}
/>
{/* Checkbox */}
<input
type="checkbox"
checked={checked}
onChange={e => setChecked(e.target.checked)}
/>
{/* Select */}
<select value={selected} onChange={e => setSelected(e.target.value)}>
<option value="option1">Option 1</option>
<option value="option2">Option 2</option>
</select>
{/* Textarea */}
<textarea
value={text}
onChange={e => setText(e.target.value)}
/>
{/* Radio buttons */}
<input
type="radio"
name="choice"
value="a"
checked={selected === 'a'}
onChange={e => setSelected(e.target.value)}
/>
</form>
);
}
```
### Uncontrolled Components with Refs
```jsx
function UncontrolledForm() {
const inputRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
console.log('Input value:', inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input ref={inputRef} type="text" defaultValue="Initial" />
<button type="submit">Submit</button>
</form>
);
}
```
---
## Context API
### Creating Context
```jsx
import { createContext, useState, useContext } from 'react';
// Create context
const ThemeContext = createContext();
// Provider component
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>
);
}
// Consumer component
function ThemedButton() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
style={{
background: theme === 'dark' ? '#333' : '#fff',
color: theme === 'dark' ? '#fff' : '#333'
}}
>
Current: {theme}
</button>
);
}
// App structure
function App() {
return (
<ThemeProvider>
<ThemedButton />
</ThemeProvider>
);
}
```
### Multiple Contexts
```jsx
const UserContext = createContext();
const ThemeContext = createContext();
function App() {
return (
<UserContext.Provider value={user}>
<ThemeContext.Provider value={theme}>
<MainApp />
</ThemeContext.Provider>
</UserContext.Provider>
);
}
function MainApp() {
const user = useContext(UserContext);
const theme = useContext(ThemeContext);
return <div>{user.name} - {theme}</div>;
}
```
---
## Refs
### DOM References
```jsx
function TextInput() {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusInput}>Focus</button>
</>
);
}
```
### Storing Mutable Values
```jsx
function Timer() {
const [seconds, setSeconds] = useState(0);
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
return (
<div>
<p>Seconds: {seconds}</p>
<button onClick={start}>Start</button>
<button onClick={stop}>Stop</button>
</div>
);
}
```
---
## Error Boundaries
### Error Boundary Component
```jsx
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
```
---
## Performance Optimization
### React.memo
```jsx
// Prevents re-render if props haven't changed
const ExpensiveComponent = React.memo(({ data }) => {
console.log('Rendering expensive component');
return <div>{data}</div>;
});
// With custom comparison
const ExpensiveComponent = React.memo(
({ data }) => <div>{data}</div>,
(prevProps, nextProps) => {
return prevProps.data.id === nextProps.data.id;
}
);
```
### useMemo & useCallback
```jsx
function OptimizedComponent({ items }) {
// Memoize expensive calculation
const sortedItems = useMemo(() => {
return items.sort((a, b) => a.value - b.value);
}, [items]);
// Memoize function
const handleClick = useCallback(() => {
console.log('Clicked');
}, []);
return <ChildComponent onClick={handleClick} items={sortedItems} />;
}
```
### Code Splitting (Lazy Loading)
```jsx
import { lazy, Suspense } from 'react';
// Lazy load component
const HeavyComponent = lazy(() => import('./HeavyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
}
```
---
## Advanced Patterns
### Higher-Order Components (HOC)
```jsx
function withAuth(Component) {
return function AuthenticatedComponent(props) {
const { isAuthenticated } = useAuth();
if (!isAuthenticated) {
return <Redirect to="/login" />;
}
return <Component {...props} />;
};
}
// Usage
const ProtectedPage = withAuth(Dashboard);
```
### Render Props
```jsx
class Mouse extends Component {
state = { x: 0, y: 0 };
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
// Usage
<Mouse render={({ x, y }) => (
<h1>Mouse position: {x}, {y}</h1>
)} />
```
### Compound Components
```jsx
function Tabs({ children }) {
const [activeTab, setActiveTab] = useState(0);
return (
<div>
{React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
isActive: index === activeTab,
onActivate: () => setActiveTab(index)
});
})}
</div>
);
}
function Tab({ isActive, onActivate, children }) {
return (
<button
onClick={onActivate}
style={{ fontWeight: isActive ? 'bold' : 'normal' }}
>
{children}
</button>
);
}
// Usage
<Tabs>
<Tab>Tab 1</Tab>
<Tab>Tab 2</Tab>
<Tab>Tab 3</Tab>
</Tabs>
```
### Portals
```jsx
import { createPortal } from 'react-dom';
function Modal({ children }) {
return createPortal(
<div className="modal">
{children}
</div>,
document.getElementById('modal-root')
);
}
// Usage
<Modal>
<h1>Modal Content</h1>
</Modal>
```
### Forward Refs
```jsx
const FancyButton = forwardRef((props, ref) => (
<button ref={ref} className="fancy-button">
{props.children}
</button>
));
// Usage
function App() {
const ref = useRef();
return <FancyButton ref={ref}>Click me</FancyButton>;
}
```
---
## PropTypes (Type Checking)
### Basic PropTypes
```jsx
import PropTypes from 'prop-types';
function Greeting({ name, age, isActive }) {
return <div>Hello {name}</div>;
}
Greeting.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
isActive: PropTypes.bool,
callback: PropTypes.func,
items: PropTypes.array,
user: PropTypes.object,
element: PropTypes.element,
node: PropTypes.node,
any: PropTypes.any
};
Greeting.defaultProps = {
age: 18,
isActive: false
};
```
### Advanced PropTypes
```jsx
MyComponent.propTypes = {
// Array of specific type
numbers: PropTypes.arrayOf(PropTypes.number),
// Object with specific shape
user: PropTypes.shape({
name: PropTypes.string,
age: PropTypes.number
}),
// Object with values of specific type
scores: PropTypes.objectOf(PropTypes.number),
// One of specific values
status: PropTypes.oneOf(['active', 'inactive', 'pending']),
// One of specific types
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number
]),
// Instance of a class
message: PropTypes.instanceOf(Message),
// Custom validator
customProp: (props, propName, componentName) => {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
};
```
---
## Best Practices
### Component Organization
```jsx
// ✅ Good: One component per file
// UserProfile.jsx
import { useState } from 'react';
import './UserProfile.css';
function UserProfile({ user }) {
const [isEditing, setIsEditing] = useState(false);
return (
<div className="user-profile">
{/* Component JSX */}
</div>
);
}
export default UserProfile;
```
### Naming Conventions
```jsx
// ✅ Components: PascalCase
function UserProfile() {}
function TodoList() {}
// ✅ Hooks: camelCase with 'use' prefix
function useAuth() {}
function useFetch() {}
// ✅ Event handlers: 'handle' prefix
const handleClick = () => {};
const handleSubmit = () => {};
// ✅ Boolean props: 'is', 'has', 'should' prefix
<Button isDisabled hasIcon shouldValidate />
```
### Props Destructuring
```jsx
// ✅ Good: Destructure props
function UserCard({ name, email, avatar }) {
return <div>{name}</div>;
}
// ❌ Avoid: Using props object
function UserCard(props) {
return <div>{props.name}</div>;
}
```
### State Management
```jsx
// ✅ Group related state
const [user, setUser] = useState({
name: '',
email: '',
age: 0
});
// ❌ Avoid: Too many separate states
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [age, setAge] = useState(0);
// ✅ Use functional updates when state depends on previous
setCount(prevCount => prevCount + 1);
// ❌ Avoid: Direct reference to current state
setCount(count + 1);
```
### Conditional Rendering
```jsx
// ✅ Good: Clear and readable
{isLoggedIn && <Dashboard />}
{error ? <ErrorMessage /> : <SuccessMessage />}
// ❌ Avoid: Nested ternaries
{isLoggedIn ? (
isAdmin ? <AdminPanel /> : <UserPanel />
) : <Login />}
// ✅ Better: Extract to function or separate component
const renderPanel = () => {
if (!isLoggedIn) return <Login />;
if (isAdmin) return <AdminPanel />;
return <UserPanel />;
};
```
### Key Props
```jsx
// ✅ Use unique IDs
{items.map(item => <div key={item.id}>{item.name}</div>)}
// ❌ Avoid index as key (when order changes)
{items.map((item, i) => <div key={i}>{item.name}</div>)}
```
### useEffect Dependencies
```jsx
// ✅ Include all dependencies
useEffect(() => {
fetchUser(userId);
}, [userId]);
// ❌ Missing dependencies
useEffect(() => {
fetchUser(userId);
}, []); // ESLint will warn
// ✅ Empty array for mount-only effect
useEffect(() => {
console.log('Component mounted');
}, []);
```
### Avoid Inline Functions in JSX
```jsx
// ✅ Good: Define function outside JSX
function MyComponent() {
const handleClick = () => {
console.log('Clicked');
};
return <button onClick={handleClick}>Click</button>;
}
// ❌ Avoid: Inline function (creates new function on each render)
function MyComponent() {
return (
<button onClick={() => console.log('Clicked')}>
Click
</button>
);
}
```
### Component Composition
```jsx
// ✅ Good: Small, focused components
function Header() {
return (
<header>
<Logo />
<Navigation />
<UserMenu />
</header>
);
}
// ❌ Avoid: Large, monolithic components
function Header() {
return (
<header>
{/* 200+ lines of JSX */}
</header>
);
}
```
---
## Common Patterns
### Loading States
```jsx
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchUser() {
try {
setLoading(true);
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
setUser(data);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
fetchUser();
}, [userId]);
if (loading) return <Spinner />;
if (error) return <Error message={error} />;
if (!user) return <NotFound />;
return <div>{user.name}</div>;
}
```
### Debouncing
```jsx
function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
const timeoutId = setTimeout(() => {
if (query) {
searchAPI(query).then(setResults);
}
}, 500); // 500ms debounce
return () => clearTimeout(timeoutId);
}, [query]);
return (
<input
value={query}
onChange={e => setQuery(e.target.value)}
placeholder="Search..."
/>
);
}
```
### Pagination
```jsx
function PaginatedList({ items }) {
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10;
const totalPages = Math.ceil(items.length / itemsPerPage);
const startIndex = (currentPage - 1) * itemsPerPage;
const currentItems = items.slice(startIndex, startIndex + itemsPerPage);
return (
<div>
<ul>
{currentItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
<div>
<button
onClick={() => setCurrentPage(p => Math.max(1, p - 1))}
disabled={currentPage === 1}
>
Previous
</button>
<span>Page {currentPage} of {totalPages}</span>
<button
onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}
disabled={currentPage === totalPages}
>
Next
</button>
</div>
</div>
);
}
```
### Modal Management
```jsx
function App() {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<div>
<button onClick={() => setIsModalOpen(true)}>
Open Modal
</button>
{isModalOpen && (
<Modal onClose={() => setIsModalOpen(false)}>
<h2>Modal Content</h2>
<button onClick={() => setIsModalOpen(false)}>
Close
</button>
</Modal>
)}
</div>
);
}
```
---
## Testing
### Basic Component Test
```jsx
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
test('button click updates count', () => {
render(<Button />);
const button = screen.getByText('Count: 0');
fireEvent.click(button);
expect(screen.getByText('Count: 1')).toBeInTheDocument();
});
```
### Testing Hooks
```jsx
import { renderHook, act } from '@testing-library/react';
import useCounter from './useCounter';
test('useCounter increments', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
```
---
## Quick Reference
### Hooks at a Glance
| Hook | Purpose |
|------|---------|
| `useState` | Add state to functional components |
| `useEffect` | Perform side effects |
| `useContext` | Access context values |
| `useReducer` | Complex state logic |
| `useRef` | Access DOM or persist values |
| `useMemo` | Memoize expensive calculations |
| `useCallback` | Memoize functions |
| `useLayoutEffect` | Synchronous effects |
| `useImperativeHandle` | Customize ref exposure |
### Common Patterns Quick Reference
```jsx
// State
const [state, setState] = useState(initialValue);
// Effect (componentDidMount + componentDidUpdate + componentWillUnmount)
useEffect(() => {
// Effect logic
return () => {
// Cleanup
};
}, [dependencies]);
// Context
const value = useContext(MyContext);
// Ref
const ref = useRef(initialValue);
// Callback
const memoizedCallback = useCallback(() => {}, [deps]);
// Memo
const memoizedValue = useMemo(() => computeExpensiveValue(), [deps]);
```
---
## Resources & Tools
### Essential Tools
- **Create React App**: Quick start for React projects
- **Next.js**: React framework with SSR/SSG
- **Vite**: Fast build tool and dev server
- **React DevTools**: Browser extension for debugging
### State Management
- **Redux**: Predictable state container
- **Zustand**: Lightweight state management
- **Recoil**: Facebook's state management library
- **Jotai**: Atomic state management
### UI Libraries
- **Material-UI (MUI)**: Material Design components
- **Chakra UI**: Simple, modular components
- **Tailwind CSS**: Utility-first CSS framework
- **Ant Design**: Enterprise UI components
### Testing
- **Jest**: JavaScript testing framework
- **React Testing Library**: Test React components
- **Cypress**: E2E testing
### Documentation
- [Official React Docs](https://react.dev)
- [React TypeScript Cheatsheet](https://react-typescript-cheatsheet.netlify.app/)
- [Awesome React](https://github.com/enaqx/awesome-react)
---
## Common Gotchas
1. **State Updates Are Asynchronous**: Use functional updates when new state depends on old state
2. **Keys in Lists**: Always use unique, stable keys for list items
3. **useEffect Dependencies**: Include all dependencies used in the effect
4. **Event Handler Binding**: Use arrow functions or bind `this` in class components
5. **Mutating State**: Never mutate state directly, always use setState/setter function
6. **Stale Closures**: Be aware of closure scope in useEffect and event handlers
7. **Props vs State**: Props are immutable, state is mutable
8. **Component Re-renders**: Understanding when and why components re-render
---
*This comprehensive guide covers React fundamentals to advanced patterns. Bookmark for quick reference during development!*