1. The Cost of JavaScript
Performance isn't just about speed; it's about user retention. A 1MB JavaScript bundle blocks the main thread, freezing the UI. We must split our code and optimize rendering.
Key Techniques
- Code Splitting (Lazy Loading): Only load the code needed for the current page.
- Virtualization: Render only the items visible on the screen (essential for long lists).
- Memoization: Prevent unnecessary re-renders using `React.memo`, `useMemo`, and `useCallback`.
Implement Code Splitting
As a mobile user with a slow connection, I want the initial page load to be fast, even if other pages take longer to load later.
- Identify heavy routes (e.g., Dashboard, Admin).
- Replace static imports with
React.lazy. - Wrap routes in
Suspensewith a fallback UI.
import { Suspense, lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import LoadingSpinner from './components/LoadingSpinner';
// Lazy load the component
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<BrowserRouter>
{/* Suspense catches the "loading" state */}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Virtualize Long Lists
As a user, I want to scroll through a list of 10,000 items smoothly without the browser crashing.
- Install
react-windoworreact-virtuoso. - Replace a standard
maplist with a virtualized list component. - Verify that only ~10-20 DOM nodes exist at any time.
import { FixedSizeList as List } from 'react-window';
const Row = ({ index, style }) => (
<div style={style} className="list-item">
Row {index}
</div>
);
const Example = () => (
<List
height={400}
itemCount={10000}
itemSize={35}
width={300}
>
{Row}
</List>
);
User Story: As a user, I want the interface to remain responsive even when performing heavy calculations.
Acceptance Criteria:
- Identify a slow function (e.g., filtering a large list).
- Wrap the calculation in
useMemo. - Wrap the callback passed to a child component in
useCallbackto prevent unnecessary re-renders of the child (if the child is wrapped inReact.memo).
Prevent XSS Attacks
As a security engineer, I want to ensure that user-generated content cannot execute malicious scripts.
- Identify any usage of
dangerouslySetInnerHTML. - Install
dompurify. - Sanitize HTML content before rendering.
import DOMPurify from 'dompurify';
interface Props {
dirtyContent: string;
}
const SafeHTML = ({ dirtyContent }: Props) => {
// Sanitize the string
const cleanContent = DOMPurify.sanitize(dirtyContent);
return (
<div
dangerouslySetInnerHTML={{ __html: cleanContent }}
/>
);
};
4. Debugging & Refactoring
Debug: Use the React Developer Tools Profiler to identify components that re-render too often. Look for "wasted renders".
Refactor: Improve codebase by breaking large components into smaller, reusable ones. Adopt new practices like migrating class components to Functional Components + Hooks.
🐛 Bug Hunt: The "Memo" Trap
Scenario: You wrapped a component in React.memo, but it still re-renders every time the parent renders.
Why? You are likely passing a new object or new function as a prop.
// ❌ Bad: New object created every render
<Child options={{ color: 'red' }} />
// ❌ Bad: New function created every render
<Child onClick={() => console.log('click')} />
Fix: Use useMemo for objects and useCallback for functions.