React Cheatsheet
A User-friendly guide to React fundamentals, hooks, and best practices. Learn how to build efficient and maintainable React applications.
Component Basics
Functional Components
Modern React components are written as functions that return JSX.
// Basic functional component
function Greeting({ name }) {
return <h1>Hello, {name}!</h1>;
}
// Arrow function component
const Profile = ({ username, bio }) => (
<div className="profile">
<h2>{username}</h2>
<p>{bio}</p>
</div>
);
// Component with props destructuring
const UserCard = ({ name, email, avatar }) => {
return (
<div className="user-card">
<img src={avatar} alt={name} />
<h3>{name}</h3>
<p>{email}</p>
</div>
);
};
JSX Rules
Essential rules and patterns for writing JSX.
// Class becomes className
const Button = () => (
<button className="btn-primary">Click me</button>
);
// Self-closing tags must be closed
const Image = () => (
<img src="image.jpg" alt="description" />
);
// Multiple elements need a parent
const Layout = () => (
<>
<Header />
<Main />
<Footer />
</>
);
// JavaScript in JSX
const Greeting = ({ user, isLoggedIn }) => (
<div>
{isLoggedIn ? (
<h1>Welcome back, {user.name}!</h1>
) : (
<h1>Please log in</h1>
)}
</div>
);
Hooks
useState
Manage local state in functional components.
// Basic state
const [count, setCount] = useState(0);
// Object state
const [user, setUser] = useState({
name: 'Aiden',
email: 'aiden@example.com'
});
// State updates
const [items, setItems] = useState([]);
// Add item
setItems([...items, newItem]);
// Update object
setUser(prev => ({
...prev,
name: 'Brooklyn'
}));
// Multiple state updates
const [count, setCount] = useState(0);
const increment = () => {
setCount(prev => prev + 1); // Use callback for
setCount(prev => prev + 1); // multiple updates
};
useEffect
Handle side effects in your components.
// Run on every render
useEffect(() => {
document.title = 'Page updated';
});
// Run only on mount
useEffect(() => {
fetchData();
}, []);
// Run when dependencies change
useEffect(() => {
console.log('Count changed:', count);
}, [count]);
// Cleanup on unmount
useEffect(() => {
const subscription = api.subscribe();
return () => {
subscription.unsubscribe();
};
}, []);
// Async operations
useEffect(() => {
const fetchData = async () => {
try {
const response = await api.getData();
setData(response);
} catch (error) {
setError(error);
}
};
fetchData();
}, []);
Custom Hooks
Creating Custom Hooks
Extract reusable logic into custom hooks.
// Form input hook
const useFormInput = (initialValue) => {
const [value, setValue] = useState(initialValue);
const handleChange = (e) => {
setValue(e.target.value);
};
return {
value,
onChange: handleChange
};
};
// Usage
function SignupForm() {
const email = useFormInput('');
const password = useFormInput('');
return (
<form>
<input {...email} type="email" />
<input {...password} type="password" />
</form>
);
}
// API hook
const useApi = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
const json = await response.json();
setData(json);
setLoading(false);
} catch (err) {
setError(err);
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
Performance
Optimization Hooks
Hooks for optimizing component performance.
// useMemo for expensive calculations
const memoizedValue = useMemo(() => {
return computeExpensiveValue(deps);
}, [deps]);
// useCallback for function memoization
const memoizedCallback = useCallback(() => {
doSomething(deps);
}, [deps]);
// React.memo for component memoization
const MemoizedComponent = React.memo(({ prop }) => {
return <div>{prop}</div>;
});
// Custom comparison function
const areEqual = (prevProps, nextProps) => {
return prevProps.id === nextProps.id;
};
const MemoizedWithComparison = React.memo(
Component,
areEqual
);
Context Optimization
Optimize context usage to prevent unnecessary renders.
// Split context by usage
const UserContext = createContext();
const ThemeContext = createContext();
// Provider composition
function App() {
return (
<UserContext.Provider value={userData}>
<ThemeContext.Provider value={theme}>
<MainContent />
</ThemeContext.Provider>
</UserContext.Provider>
);
}
// Context selector pattern
function UserProfile() {
const { user } = useContext(UserContext);
return <div>{user.name}</div>;
}
// Context with reducer
const [state, dispatch] = useReducer(reducer, initialState);
return (
<AppContext.Provider value={{ state, dispatch }}>
<App />
</AppContext.Provider>
);
Error Handling
Error Boundaries
Handle and display errors gracefully.
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
// Try-Catch in Effects
useEffect(() => {
const fetchData = async () => {
try {
const data = await api.getData();
setData(data);
} catch (error) {
setError(error);
showErrorNotification();
}
};
fetchData();
}, []);
Props & PropTypes
Props Patterns
Common patterns for handling props in React components.
// Default props
const Button = ({ type = 'button', children }) => (
<button type={type}>{children}</button>
);
// Props spreading
const Input = ({ className, ...props }) => (
<input
className={`default-input ${className}`}
{...props}
/>
);
// Prop Types
import PropTypes from 'prop-types';
function UserProfile({ user, onUpdate }) {
return (
<div>
<h2>{user.name}</h2>
<button onClick={() => onUpdate(user.id)}>
Update Profile
</button>
</div>
);
}
UserProfile.propTypes = {
user: PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired,
email: PropTypes.string
}).isRequired,
onUpdate: PropTypes.func.isRequired
};
State Management
useReducer
Complex state management using reducer pattern.
// Define reducer
const reducer = (state, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
// Use reducer in component
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
Count: {state.count}
<button onClick={() => dispatch({ type: 'INCREMENT' })}>
Add
</button>
<button onClick={() => dispatch({ type: 'DECREMENT' })}>
Subtract
</button>
</div>
);
}
// Complex state example
const todoReducer = (state, action) => {
switch (action.type) {
case 'ADD_TODO':
return [...state, action.payload];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
);
case 'DELETE_TODO':
return state.filter(todo => todo.id !== action.payload);
default:
return state;
}
};
Context API
Global state management using Context API.
// Create context
const ThemeContext = createContext();
// Create provider
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>
);
}
// Custom hook for using theme
function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within ThemeProvider');
}
return context;
}
// Usage in components
function ThemedButton() {
const { theme, toggleTheme } = useTheme();
return (
<button
className={`btn-${theme}`}
onClick={toggleTheme}
>
Toggle Theme
</button>
);
}
Component Lifecycle
Lifecycle with Hooks
Managing component lifecycle using hooks.
// Component Mount
function ComponentMount() {
useEffect(() => {
console.log('Component mounted');
return () => console.log('Component unmounted');
}, []);
return <div>Mounted Component</div>;
}
// Before Update
const ComponentUpdate = ({ data }) => {
const prevData = useRef(data);
useEffect(() => {
if (prevData.current !== data) {
console.log('Data changed:', {
from: prevData.current,
to: data
});
}
prevData.current = data;
}, [data]);
return <div>Updated Component</div>;
}
// Preventing Updates
const MemoizedComponent = React.memo(({ data }) => {
console.log('Only renders if data changes');
return <div>{data}</div>;
});
Advanced useEffect
Complex useEffect patterns and cleanup.
// Debounced effect
function DebouncedSearch() {
const [search, setSearch] = useState('');
const [results, setResults] = useState([]);
useEffect(() => {
const debounceTimeout = setTimeout(async () => {
if (search) {
const data = await searchApi(search);
setResults(data);
}
}, 500);
return () => clearTimeout(debounceTimeout);
}, [search]);
return (
<input
value={search}
onChange={e => setSearch(e.target.value)}
/>
);
}
// Event listener cleanup
function EventComponent() {
useEffect(() => {
const handleScroll = () => {
console.log('Scrolled');
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return <div>Scroll Listener</div>;
}
Forms & Refs
Form Handling
Managing form state and validation in React.
// Controlled form
function SignupForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: ''
});
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const validate = () => {
const newErrors = {};
if (!formData.username) {
newErrors.username = 'Username is required';
}
if (!formData.email.includes('@')) {
newErrors.email = 'Invalid email';
}
return newErrors;
};
const handleSubmit = (e) => {
e.preventDefault();
const newErrors = validate();
if (Object.keys(newErrors).length === 0) {
// Submit form
} else {
setErrors(newErrors);
}
};
return (
<form onSubmit={handleSubmit}>
<input
name="username"
value={formData.username}
onChange={handleChange}
/>
{errors.username && (
<span className="error">{errors.username}</span>
)}
{/* Other fields */}
</form>
);
}
useRef Patterns
Common useRef patterns and use cases.
// DOM reference
function FocusInput() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, []);
return <input ref={inputRef} />;
}
// Previous value reference
function Counter() {
const [count, setCount] = useState(0);
const prevCount = useRef(count);
useEffect(() => {
prevCount.current = count;
}, [count]);
return (
<div>
Now: {count}, Before: {prevCount.current}
</div>
);
}
// Instance values
function Timer() {
const [count, setCount] = useState(0);
const intervalRef = useRef();
useEffect(() => {
intervalRef.current = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(intervalRef.current);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => clearInterval(intervalRef.current)}>
Stop
</button>
</div>
);
}
Advanced Patterns
Render Props
Share code between components using a prop whose value is a function.
// Render prop component
const MouseTracker = ({ render }) => {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (event) => {
setPosition({
x: event.clientX,
y: event.clientY
});
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
return render(position);
};
// Usage
<MouseTracker
render={({ x, y }) => (
<div>
Mouse position: {x}, {y}
</div>
)}
/>
Higher-Order Components (HOC)
Functions that take a component and return a new enhanced component.
// HOC example
const withLogger = (WrappedComponent) => {
return function WithLogger(props) {
useEffect(() => {
console.log('Component mounted:', WrappedComponent.name);
return () => console.log('Component unmounted:', WrappedComponent.name);
}, []);
return <WrappedComponent {...props} />;
};
};
// Usage
const MyComponent = ({ name }) => <div>Hello {name}</div>;
const EnhancedComponent = withLogger(MyComponent);
// HOC with configuration
const withAuth = (requiredRole) => (WrappedComponent) => {
return function WithAuth(props) {
const { role } = useAuth();
if (role !== requiredRole) {
return <AccessDenied />;
}
return <WrappedComponent {...props} />;
};
};
React Patterns
Compound Components
Create components that work together to form a complete UI.
// Compound components pattern
const Select = ({ children, onChange }) => {
const [selectedOption, setSelectedOption] = useState(null);
const handleSelect = (option) => {
setSelectedOption(option);
onChange?.(option);
};
return (
<SelectContext.Provider value={{ selectedOption, onSelect: handleSelect }}>
<div className="select">{children}</div>
</SelectContext.Provider>
);
};
Select.Option = ({ value, children }) => {
const { selectedOption, onSelect } = useContext(SelectContext);
return (
<div
className={`option ${selectedOption === value ? 'selected' : ''}`}
onClick={() => onSelect(value)}
>
{children}
</div>
);
};
// Usage
<Select onChange={handleChange}>
<Select.Option value="1">Option 1</Select.Option>
<Select.Option value="2">Option 2</Select.Option>
</Select>
Custom Hooks Patterns
Advanced patterns for creating reusable hooks.
// Composable hooks
const useCounter = (initialValue = 0) => {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(initialValue);
return { count, increment, decrement, reset };
};
// Hook with callbacks
const useEventListener = (eventName, handler, element = window) => {
const savedHandler = useRef();
useEffect(() => {
savedHandler.current = handler;
}, [handler]);
useEffect(() => {
const eventListener = event => savedHandler.current(event);
element.addEventListener(eventName, eventListener);
return () => element.removeEventListener(eventName, eventListener);
}, [eventName, element]);
};
// Async hook with cleanup
const useAsync = (asyncFn, immediate = true) => {
const [status, setStatus] = useState('idle');
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const execute = useCallback(async () => {
setStatus('pending');
try {
const response = await asyncFn();
setData(response);
setStatus('success');
} catch (err) {
setError(err);
setStatus('error');
}
}, [asyncFn]);
useEffect(() => {
if (immediate) {
execute();
}
}, [execute, immediate]);
return { execute, status, data, error };
};
Performance Techniques
Code Splitting
Split your code into smaller chunks for better performance.
// Dynamic imports
const MyComponent = React.lazy(() => import('./MyComponent'));
// Suspense boundary
function App() {
return (
<Suspense fallback={<Loading />}>
<MyComponent />
</Suspense>
);
}
// Route-based code splitting
import { BrowserRouter, Route, Switch } from 'react-router-dom';
const Home = React.lazy(() => import('./routes/Home'));
const Dashboard = React.lazy(() => import('./routes/Dashboard'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<Loading />}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/dashboard" component={Dashboard} />
</Switch>
</Suspense>
</BrowserRouter>
);
}
Virtual List
Render large lists efficiently using virtualization.
// Virtual list implementation
function VirtualList({ items, rowHeight, visibleRows }) {
const [scrollTop, setScrollTop] = useState(0);
const containerRef = useRef();
const startIndex = Math.floor(scrollTop / rowHeight);
const endIndex = Math.min(
startIndex + visibleRows,
items.length
);
const visibleItems = items.slice(startIndex, endIndex);
const totalHeight = items.length * rowHeight;
const offsetY = startIndex * rowHeight;
return (
<div
ref={containerRef}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
style={{ height: visibleRows * rowHeight, overflow: 'auto' }}
>
<div style={{ height: totalHeight }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map(item => (
<div key={item.id} style={{ height: rowHeight }}>
{item.content}
</div>
))}
</div>
</div>
</div>
);
}