1. React.memo
import { memo } from 'react';
// Memoize component — chỉ re-render khi props thay đổi
const ExpensiveList = memo(function ExpensiveList({ items }: { items: Item[] }) {
console.log('ExpensiveList rendered');
return (
<ul>
{items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
});
// Custom comparison
const UserCard = memo(
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => {
return prevProps.user.id === nextProps.user.id
&& prevProps.user.name === nextProps.user.name;
}
);
2. Higher-Order Components (HOC)
// withAuth HOC
function withAuth<T extends object>(Component: React.ComponentType<T>) {
return function AuthenticatedComponent(props: T) {
const { user, isLoading } = useAuth();
if (isLoading) return <Spinner />;
if (!user) return <Navigate to="/login" />;
return <Component {...props} />;
};
}
// Usage
const ProtectedDashboard = withAuth(Dashboard);
// withLogging HOC
function withLogging<T extends object>(Component: React.ComponentType<T>, name: string) {
return function LoggedComponent(props: T) {
useEffect(() => {
console.log(`${name} mounted`);
return () => console.log(`${name} unmounted`);
}, []);
return <Component {...props} />;
};
}
3. Render Props
interface MouseTrackerProps {
render: (position: { x: number; y: number }) => React.ReactNode;
}
function MouseTracker({ render }: MouseTrackerProps) {
const [position, setPosition] = useState({ x: 0, y: 0 });
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
setPosition({ x: e.clientX, y: e.clientY });
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
}, []);
return <div>{render(position)}</div>;
}
// Usage
<MouseTracker
render={({ x, y }) => (
<p>Mouse: {x}, {y}</p>
)}
/>
4. Performance Patterns
// ❌ Anti-pattern: Object/array creation in render
function Bad() {
return <Child style={{ color: 'red' }} items={[1, 2, 3]} />; // New ref every render!
}
// ✅ Fix: useMemo or hoist outside
const style = { color: 'red' };
const items = [1, 2, 3];
function Good() {
return <Child style={style} items={items} />;
}
// ❌ Anti-pattern: Inline function as prop to memoized child
function Bad2() {
return <MemoizedChild onClick={() => doSomething()} />;
}
// ✅ Fix: useCallback
function Good2() {
const handleClick = useCallback(() => doSomething(), []);
return <MemoizedChild onClick={handleClick} />;
}
// ❌ Anti-pattern: State too high in tree
// ✅ Fix: Lift state only as needed, or co-locate state
5. Code Splitting & Lazy Loading
import { lazy, Suspense } from 'react';
// Lazy load components
const AdminPanel = lazy(() => import('./AdminPanel'));
const Analytics = lazy(() => import('./Analytics'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/admin" element={<AdminPanel />} />
<Route path="/analytics" element={<Analytics />} />
</Routes>
</Suspense>
);
}
6. React DevTools Profiler
- Mở React DevTools → Profiler tab
- Click "Record" → interact → "Stop"
- Xem flame graph: components nào render lâu nhất
- Xem "Why did this render?" cho từng component
- Highlight updates: Settings → Highlight updates → thấy components nào re-render
Bài tiếp theo: Styling & UI Components — Tailwind CSS, shadcn/ui, và Framer Motion animations.