Performance Optimization
Optimize React application performance.
Overview
This guide covers performance optimization techniques for EZ-Console React applications, including code splitting, memoization, lazy loading, and best practices.
Code Splitting
Route-Based Code Splitting
Routes are automatically lazy-loaded:
// Routes are lazy-loaded by default
const ProductList = lazy(() => import('@/pages/products/ProductList'));
const ProductForm = lazy(() => import('@/pages/products/ProductForm'));
// Wrap with Suspense
export function withSuspense(Component: React.LazyExoticComponent<any>) {
return (
<Suspense fallback={<Loading />}>
<Component />
</Suspense>
);
}
Component-Based Code Splitting
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
export const MyComponent: React.FC = () => {
const [showHeavy, setShowHeavy] = useState(false);
return (
<div>
<Button onClick={() => setShowHeavy(true)}>Load Heavy Component</Button>
{showHeavy && (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
)}
</div>
);
};
Memoization
React.memo
Prevent unnecessary re-renders:
import { memo } from 'react';
export const ProductCard = memo<ProductCardProps>(({ product, onEdit }) => {
return (
<Card>
<h3>{product.name}</h3>
<Button onClick={() => onEdit(product.id)}>Edit</Button>
</Card>
);
}, (prevProps, nextProps) => {
// Custom comparison
return prevProps.product.id === nextProps.product.id;
});
useMemo
Memoize expensive calculations:
import { useMemo } from 'react';
export const ProductList: React.FC<{ products: Product[] }> = ({ products }) => {
const expensiveValue = useMemo(() => {
// Expensive calculation
return products.reduce((sum, p) => sum + p.price, 0);
}, [products]);
return <div>Total: {expensiveValue}</div>;
};
useCallback
Memoize callback functions:
import { useCallback } from 'react';
export const ProductList: React.FC = () => {
const handleEdit = useCallback((id: string) => {
navigate(`/products/${id}/edit`);
}, [navigate]);
return (
<div>
{products.map(product => (
<ProductCard
key={product.id}
product={product}
onEdit={handleEdit}
/>
))}
</div>
);
};
React Query Optimization
Query Caching
import { useQuery } from 'react-query';
export const ProductList: React.FC = () => {
const { data } = useQuery(
'products',
() => apiGet('/products'),
{
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 10 * 60 * 1000, // 10 minutes
}
);
return <div>{/* Render products */}</div>;
};
Pagination
import { useInfiniteQuery } from 'react-query';
export const ProductList: React.FC = () => {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery(
'products',
({ pageParam = 1 }) => apiGet('/products', { params: { page: pageParam } }),
{
getNextPageParam: (lastPage, pages) => {
return lastPage.hasMore ? pages.length + 1 : undefined;
},
}
);
return (
<div>
{data?.pages.map((page, i) => (
<div key={i}>
{page.data.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
))}
{hasNextPage && (
<Button onClick={() => fetchNextPage()} loading={isFetchingNextPage}>
Load More
</Button>
)}
</div>
);
};
Virtual Scrolling
For large lists, use virtual scrolling:
import { FixedSizeList } from 'react-window';
export const ProductList: React.FC<{ products: Product[] }> = ({ products }) => {
const Row = ({ index, style }: any) => (
<div style={style}>
<ProductCard product={products[index]} />
</div>
);
return (
<FixedSizeList
height={600}
itemCount={products.length}
itemSize={100}
width="100%"
>
{Row}
</FixedSizeList>
);
};
Image Optimization
Lazy Loading Images
import { LazyLoadImage } from 'react-lazy-load-image-component';
export const ProductImage: React.FC<{ src: string; alt: string }> = ({ src, alt }) => {
return (
<LazyLoadImage
src={src}
alt={alt}
effect="blur"
placeholder={<div style={{ background: '#f0f0f0' }} />}
/>
);
};
Bundle Optimization
Analyze Bundle Size
# Install analyzer
pnpm add -D @craco/craco craco-bundle-analyzer
# Analyze
pnpm build --analyze
Tree Shaking
Ensure you're importing only what you need:
// ✅ Good: Named imports
import { Button, Input } from 'antd';
// ❌ Bad: Import entire library
import * as Antd from 'antd';
Performance Monitoring
React DevTools Profiler
Use React DevTools Profiler to identify performance bottlenecks.
Web Vitals
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric: any) {
// Send to analytics
console.log(metric);
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
Best Practices
1. Avoid Unnecessary Re-renders
// ✅ Good: Memoize components
const ProductCard = memo(({ product }) => {
return <div>{product.name}</div>;
});
// ❌ Bad: Re-render on every parent update
const ProductCard = ({ product }) => {
return <div>{product.name}</div>;
};
2. Use Keys Properly
// ✅ Good: Stable keys
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
// ❌ Bad: Index as key
{products.map((product, index) => (
<ProductCard key={index} product={product} />
))}
3. Debounce Search Inputs
import { useDebounce } from 'ahooks';
export const SearchInput: React.FC = () => {
const [search, setSearch] = useState('');
const debouncedSearch = useDebounce(search, { wait: 300 });
useEffect(() => {
// Search API call
searchProducts(debouncedSearch);
}, [debouncedSearch]);
return <Input value={search} onChange={(e) => setSearch(e.target.value)} />;
};
4. Optimize Images
// ✅ Good: Optimized images
<img src={optimizedImage} loading="lazy" alt="Product" />
// ❌ Bad: Large unoptimized images
<img src={largeImage} alt="Product" />
Related Topics
- State Management - Optimize state updates
- Routing & Navigation - Code splitting
Need help? Ask in GitHub Discussions.