Higher-Order Components — React

Bayangin kamu nemu codebase legacy: ada yang dibungkus jadi withAuth(withLoading(withTheme(UserList))) — empat lapis HOC sebelum komponen aslinya kerja. Apa…

Higher-Order Component (HOC) adalah function yang menerima sebuah komponen, lalu mengembalikan komponen baru yang membungkusnya dengan sesuatu tambahan: kerangka, pengecekan, atau props ekstra. Komponen aslinya gak diubah sama sekali. Mirip tukang bingkai foto: kamu serahkan foto apa pun, dia kembalikan foto yang sudah berbingkai. Foto aslimu tetap ada tanpa bingkai, dan bingkai yang sama bisa dipakai untuk foto lain.

🧪 Tebakan cepat: sesudah const GreetingWithWrapper = withWrapper(Greeting), kamu tetap merender <Greeting nama="Budi" /> yang biasa. Apakah sapaannya sekarang ikut terbungkus <div id="wrapper">? Tebak dulu, jawabannya ada di bagian "Function yang menerima komponen".

Function yang menerima komponen

Mulai dari komponen biasa yang menyapa lewat prop nama:

function Greeting({ nama }) {
  return <p id="greeting">Halo, {nama}!</p>
}

HOC-nya, withWrapper, menaruh sapaan itu di dalam sebuah kerangka:

function withWrapper(Component) {
  return function WithWrapper(props) {
    return (
      <div id="wrapper">
        <Component {...props} />
      </div>
    )
  }
}

// dipanggil sekali, di luar komponen mana pun
const GreetingWithWrapper = withWrapper(Greeting)

function App() {
  return <GreetingWithWrapper nama="Budi" />
}

Greeting sendiri gak berubah. <Greeting nama="Budi" /> tetap merender <p> saja, tanpa div. Yang terbungkus cuma komponen baru, GreetingWithWrapper. Itu jawaban tebakannya.

Jalannya props, dari App sampai layar

Langkah Komponen Props yang diterima Yang dirender
1 App (gak ada) <GreetingWithWrapper nama="Budi" />
2 WithWrapper { nama: "Budi" } <div id="wrapper"> berisi <Greeting {...props} />
3 Greeting { nama: "Budi" } <p id="greeting">Halo, Budi!</p>

Hasil akhirnya di halaman:

<div id="wrapper">
  <p id="greeting">Halo, Budi!</p>
</div>

Pohon komponennya jadi dua lapis: WithWrapper di luar, Greeting di dalam. Kalau langkah 2 menulis <Component /> tanpa {...props}, Greeting menerima {} dan sapaannya jadi Halo, !.

HOC yang menambah logika

HOC gak cuma menambah kerangka. withLoading memutuskan apakah komponen aslinya boleh dirender:

function withLoading(Component) {
  return function WithLoading({ isLoading, ...props }) {
    if (isLoading) {
      return <p>Loading...</p>
    }
    return <Component {...props} />
  }
}

const EnhancedGreeting = withLoading(withWrapper(Greeting))

// <EnhancedGreeting nama="Budi" isLoading={true} />   tampil: Loading...
// <EnhancedGreeting nama="Budi" isLoading={false} />  tampil: Halo, Budi! di dalam div#wrapper

Aturan menulis HOC

Kenapa sekarang jarang dipakai: custom hook

HOC populer sebelum hooks ada (sebelum React 16.8, tahun 2019). Sekarang, kalau yang mau dipakai ulang adalah logika (state, effect), caranya custom hook: function berawalan use yang dipanggil langsung di dalam komponen. Kamu akan membuatnya dua pelajaran lagi. Bandingkan bentuknya:

// HOC: data user disuntikkan sebagai prop, lewat satu lapis komponen tambahan
const ProfileWithUser = withUser(Profile)

// Custom hook: dipanggil langsung di komponen, gak ada lapis tambahan
function Profile() {
  const user = useUser()
  return <p>Halo, {user.nama}!</p>
}

⚠️ Jebakan yang sering ketemu

TL;DR: HOC adalah function yang menerima komponen dan mengembalikan komponen baru yang membungkusnya. Namanya diawali with, parameternya biasa dinamai Component, dan komponen baru itu merender <Component {...props} /> supaya semua props tetap sampai. Komponen asli gak berubah. Panggil HOC sekali di luar komponen: const GreetingWithWrapper = withWrapper(Greeting). HOC bisa menambah kerangka (withWrapper) atau logika (withLoading), dan bisa ditumpuk, tiap HOC satu lapis. Di kode baru, logika yang dipakai ulang biasanya ditulis sebagai custom hook; HOC masih kamu temui di kode dan library lama.

Yang akan kamu pelajari