Bayangin kamu nemu codebase legacy: ada <UserList> yang dibungkus jadi withAuth(withLoading(withTheme(UserList))) — empat lapis HOC sebelum komponen aslinya kerja. Apa itu? Higher-Order Component (HOC) adalah pattern reuse logic di React era pre-hooks (2014-2018): function yang nerima component, return component baru yang diperkaya dengan extra functionality. Mirip decorator di Python atau middleware di Express. Sekarang? Hooks udah ngantiin HOC di kebanyakan kasus — lebih simple, lebih type-friendly. Tapi HOC tetep wajib dipelajari karena banyak library lama (Redux connect, React Router withRouter, MobX observer) masih pakai pola ini.
⚠️ Pola lama, masih relevan. HOC bukan deprecated, cuma jarang jadi pilihan pertama di kode baru. Skill ini WAJIB kalau kamu kerja di project React 2-5 tahun yang lalu — banyak codebase company di Indonesia masih pake. Plus paham HOC = paham fundamental composition pattern.
Anatomi HOC
function withLoading(WrappedComponent) {
return function(props) {
if (props.isLoading) {
return <div>Loading...</div>
}
return <WrappedComponent {...props} />
}
}
// Pemakaian
const UserListWithLoading = withLoading(UserList)
// <UserListWithLoading isLoading={true} users={[...]} />
withLoading = HOC. Terima component, return component baru yang punya behavior tambahan (cek isLoading sebelum render).
Pattern Umum HOC
// withAuth — cek user sebelum render
function withAuth(Component) {
return function(props) {
const { user } = useContext(AuthContext)
if (!user) return <LoginPage />
return <Component {...props} user={user} />
}
}
// withErrorBoundary — wrap dengan error boundary
function withErrorBoundary(Component, FallbackUI) {
return function(props) {
return (
<ErrorBoundary fallback={FallbackUI}>
<Component {...props} />
</ErrorBoundary>
)
}
}
// withLogger — log lifecycle
function withLogger(Component, name) {
return function(props) {
useEffect(() => {
console.log(`${name} mounted`)
return () => console.log(`${name} unmounted`)
}, [])
return <Component {...props} />
}
}
Compose Multiple HOC
const EnhancedComponent = withAuth(withLoading(withTheme(UserList)))
// Atau pakai compose helper:
import { compose } from "lodash/fp"
const enhance = compose(withAuth, withLoading, withTheme)
const EnhancedComponent = enhance(UserList)
HOC vs Custom Hook — Side by Side
// HOC
function withMousePosition(Component) {
return function(props) {
const [pos, setPos] = useState({ x: 0, y: 0 })
useEffect(() => {
const handler = (e) => setPos({ x: e.clientX, y: e.clientY })
window.addEventListener("mousemove", handler)
return () => window.removeEventListener("mousemove", handler)
}, [])
return <Component {...props} mousePosition={pos} />
}
}
const Tracker = withMousePosition(MyComponent)
// CUSTOM HOOK — modern, simple
function useMousePosition() {
const [pos, setPos] = useState({ x: 0, y: 0 })
useEffect(() => {
const handler = (e) => setPos({ x: e.clientX, y: e.clientY })
window.addEventListener("mousemove", handler)
return () => window.removeEventListener("mousemove", handler)
}, [])
return pos
}
function Tracker() {
const pos = useMousePosition() // ← langsung pakai
return <div>x: {pos.x}</div>
}
Hook lebih simple, no wrapping, type signature lebih jelas.
Konvensi HOC
- Nama mulai dengan
with—withAuth,withTheme,withRouter - Forward semua props —
<WrappedComponent {...props} /> - Jangan mutasi component asli — return component baru
- Set
displayNameuntuk debugging —WithLoading.displayName =WithLoading(${Component.displayName})``
🎭 Analogi sehari-hari
HOC itu kayak bungkus kado berlapis. Komponen aslinya = mainan favorit anak. withLoading = lapis kertas bungkus pertama (handle "tunggu, lagi siap-siap"). withAuth = lapis kedua (cek "kamu udah ulang tahun belum"). withTheme = lapis ketiga (warna pita sesuai mood). User akhirnya buka bungkus kado satu-satu, mainan keluar di akhir. Custom hook lebih kayak alat masak yang dipake langsung: gak perlu bungkus tambahan, kasih ke chef (component) sebagai tools yang bisa dipake. HOC = pattern lama yang masih jalan, hooks = pattern baru yang lebih efisien.
⚠️ Jebakan yang sering ditemui
- Wrapper hell —
withA(withB(withC(withD(Component))))= nested deep, susah debug. Gunain compose helper atau pindah ke hooks. - Lupa forward props —
withSomething(C)return<C />tanpa{...props}= props hilang. - Static methods hilang — HOC gak forward static methods (
Component.fetchData()) — pakaihoist-non-react-statics. - Ref tidak ter-forward —
<EnhancedComponent ref={...}>— wrapper component yang dapat ref, bukan inner. PakaiforwardRef. - TypeScript HOC = nightmare — type inference rumit, error message mistis. Hook + TypeScript = relatively clean.
- Performance — tiap HOC = layer wrapper component. 5 HOC = 5 extra component di tree, slight overhead.
- Conditional HOC application — bingung, fragile. Hook condition lebih clean.
🎯 Pakai HOC atau Custom Hook?
- Project baru, butuh reuse logic → custom hook ✓ (default modern)
- Project legacy yang udah pakai HOC → tetap HOC (consistency)
- Wrap component dengan render UI tambahan (loading screen, auth wall) → HOC bisa lebih natural
- Inject props ke component → HOC ATAU hook + render prop
- Cross-cutting concern (logging, analytics) → HOC lebih cocok untuk inject di tingkat component
- Library wrapper (Redux connect, React Query withQueries) → masih HOC (existing API)
Aturan modern: hooks first. HOC cuma pas hook gak fit (component-level wrapping, conditional render gating).
TL;DR: HOC = function yang nerima component, return component baru dengan extra functionality. Pattern lama (pre-hooks) untuk reuse logic. Convention: nama mulai with, forward props ({...props}), jangan mutate. Modern alternative: Custom Hooks lebih simple + type-friendly. HOC tetep relevan untuk: codebase legacy, library wrapper, component-level wrapping (auth, error boundary). Skill wajib pas maintain project React 2-5 tahun lalu.