React.memo for Component Memoization
const ExpensiveComponent = React.memo(({ data, onAction }) => {
return (
<div>
{data.map(item => (
<div key={item.id}>{item.name}</div>
))}
<button onClick={onAction}>Action</button>
</div>
);
});
const CustomMemoComponent = React.memo(
({ data }) => <div>{data.value}</div>,
(prevProps, nextProps) => prevProps.data.id === nextProps.data.id
);
useMemo for Expensive Calculations
function DataProcessor({ items, filter }) {
const processedData = useMemo(() => {
return items
.filter(item => item.category === filter)
.map(item => ({
...item,
processed: expensiveCalculation(item)
}));
}, [items, filter]);
return <div>{processedData.map(item => <Item key={item.id} {...item} />)}</div>;
}
function ParentComponent({ userId }) {
const userConfig = useMemo(() => ({
id: userId,
theme: 'dark',
preferences: { notifications: true }
}), [userId]);
return <ChildComponent config={userConfig} />;
}
useCallback for Function Memoization
function ListComponent({ items }) {
const [selectedId, setSelectedId] = useState(null);
const handleItemClick = useCallback((id) => {
setSelectedId(id);
}, []);
const handleItemDelete = useCallback((id) => {
}, []);
return (
<ul>
{items.map(item => (
<ListItem
key={item.id}
item={item}
onClick={handleItemClick}
onDelete={handleItemDelete}
isSelected={selectedId === item.id}
/>
))}
</ul>
);
}
Local State Optimization
function OptimizedCounter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(prev => prev + 1);
}, []);
const reset = useCallback(() => {
setCount(0);
}, []);
return (
<div>
<span>{count}</span>
<button onClick={increment}>+</button>
<button onClick={reset}>Reset</button>
</div>
);
}
Context Optimization
const UserContext = createContext();
const ThemeContext = createContext();
function App() {
const [user, setUser] = useState(null);
const [theme, setTheme] = useState('light');
return (
<UserContext.Provider value={{ user, setUser }}>
<ThemeContext.Provider value={{ theme, setTheme }}>
<Header />
<MainContent />
</ThemeContext.Provider>
</UserContext.Provider>
);
}
function Header() {
const { user } = useContext(UserContext);
const { theme } = useContext(ThemeContext);
return <header className={theme}>{user?.name}</header>;
}
Reducer Pattern for Complex State
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload
? { ...todo, completed: !todo.completed }
: todo
)
};
default:
return state;
}
}
function TodoApp() {
const [state, dispatch] = useReducer(todoReducer, {
todos: [],
filter: 'all'
});
const addTodo = useCallback((text) => {
dispatch({ type: 'ADD_TODO', payload: { id: Date.now(), text } });
}, []);
const toggleTodo = useCallback((id) => {
dispatch({ type: 'TOGGLE_TODO', payload: id });
}, []);
return (
<div>
<TodoForm onAdd={addTodo} />
<TodoList todos={state.todos} onToggle={toggleTodo} />
</div>
);
}
Virtualization for Large Lists
import { FixedSizeList as List } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
<ListItem item={items[index]} />
</div>
);
return (
<List
height={400}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</List>
);
}
function useVirtualization(items, itemHeight, containerHeight) {
const [scrollTop, setScrollTop] = useState(0);
const visibleItems = useMemo(() => {
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight),
items.length
);
return items.slice(startIndex, endIndex).map((item, index) => ({
...item,
index: startIndex + index,
style: {
position: 'absolute',
top: (startIndex + index) * itemHeight,
height: itemHeight
}
}));
}, [items, scrollTop, itemHeight, containerHeight]);
return { visibleItems, setScrollTop };
}
Pagination and Infinite Scroll
function useInfiniteScroll(fetchData, deps = []) {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [page, setPage] = useState(1);
const loadMore = useCallback(async () => {
if (loading || !hasMore) return;
setLoading(true);
try {
const newData = await fetchData(page);
setData(prev => [...prev, ...newData]);
setHasMore(newData.length > 0);
setPage(prev => prev + 1);
} finally {
setLoading(false);
}
}, [fetchData, page, loading, hasMore]);
useEffect(() => {
loadMore();
}, deps);
return { data, loading, hasMore, loadMore };
}
Dynamic Imports
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const ChartComponent = lazy(() => import('./ChartComponent'));
function App() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>Show Chart</button>
{showChart && (
<Suspense fallback={<div>Loading chart...</div>}>
<ChartComponent />
</Suspense>
)}
</div>
);
}
const routes = {
'/dashboard': lazy(() => import('./pages/Dashboard')),
'/analytics': lazy(() => import('./pages/Analytics')),
'/settings': lazy(() => import('./pages/Settings'))
};
Tree Shaking Optimization
import { useState, useEffect } from 'react';
import { debounce } from 'lodash-es';
import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle';
Lazy Loading Images
function LazyImage({ src, alt, placeholder }) {
const [isLoaded, setIsLoaded] = useState(false);
const [imageSrc, setImageSrc] = useState(placeholder);
useEffect(() => {
const img = new Image();
img.src = src;
img.onload = () => {
setImageSrc(src);
setIsLoaded(true);
};
}, [src]);
return (
<img
src={imageSrc}
alt={alt}
className={`lazy-image ${isLoaded ? 'loaded' : ''}`}
/>
);
}
function useIntersectionObserver(ref, options = {}) {
const [isIntersecting, setIsIntersecting] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
setIsIntersecting(entry.isIntersecting);
}, options);
if (ref.current) {
observer.observe(ref.current);
}
return () => observer.disconnect();
}, [ref, options]);
return isIntersecting;
}
Cleanup and Memory Leaks
function useInterval(callback, delay) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (delay !== null) {
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}
}, [delay]);
}
function ComponentWithCleanup() {
const [data, setData] = useState([]);
const abortController = useRef();
useEffect(() => {
abortController.current = new AbortController();
fetch('/api/data', { signal: abortController.current.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') {
console.error(err);
}
});
return () => {
abortController.current?.abort();
};
}, []);
return <div>{data.map(item => <div key={item.id}>{item.name}</div>)}</div>;
}
React DevTools Profiler
import { Profiler } from 'react';
function onRenderCallback(id, phase, actualDuration, baseDuration, startTime, commitTime) {
console.log({
id,
phase,
actualDuration,
baseDuration,
startTime,
commitTime
});
}
function App() {
return (
<Profiler id="App" onRender={onRenderCallback}>
<MainContent />
</Profiler>
);
}
Custom Performance Hooks
function usePerformanceMonitor(componentName) {
const renderCount = useRef(0);
useEffect(() => {
renderCount.current += 1;
console.log(`${componentName} rendered ${renderCount.current} times`);
});
return renderCount.current;
}
function useRenderTime() {
const startTime = useRef(performance.now());
useEffect(() => {
const endTime = performance.now();
console.log(`Render took ${endTime - startTime.current}ms`);
startTime.current = performance.now();
});
}
Avoid Common Pitfalls
const expensiveValue = useMemo(() => {
return heavyComputation(data);
}, [data]);
const expensiveValue = heavyComputation(data);
const inputRef = useCallback(node => {
if (node) {
node.focus();
}
}, []);
const inputRef = useRef();
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
Bundle Size Optimization
const moment = await import('moment');
import { format } from 'date-fns';
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
Production Optimizations
function App() {
return (
<React.StrictMode>
<MainApp />
</React.StrictMode>
);
}