Skip to main content

Performance Optimization

DEVELOPER Advanced

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" />

Need help? Ask in GitHub Discussions.