diskusi.tech (beta) Community

loading...

Jangan Langsung Consume Data Eksternal pada Component React

itsfaqih profile image Muhammad Faqih Muntashir ・3 min read

Selama saya bekerja menggunakan React, saya telah beberapa kali mengubah paradigma bagaimana menghubungkan component dengan data eksternal:

Consume pada Root Component, lalu turunkan melalui props

Ini adalah cara pertama yang saya pelajari karena pada awal saya belajar React, saya dikenalkan dengan metodologi Atomic Design.

Disclaimer: Metodologi ini sama sekali tidak mengatur detail implementasi tentang bagaimana component dibuat pada React. Jadi ini murni hanya interpretasi saya saat mempelajari Atomic Design.

Saat mempelajari metodologi tersebut, tertanam dalam mindset saya bahwa baiknya component tidak langsung memiliki data tersendiri, melainkan diisi oleh component terbesar dan terluar, yaitu Page, sehingga component tersebut benar-benar dapat digunakan ulang, dan cukup mengubah sumber data dari satu tempat.

// Page
export function DashboardPage() {
  const metrics = useMetricsData();
  const user = useUserData();

  return <DashboardTemplate metrics={metrics} user={user} />;
}
Enter fullscreen mode Exit fullscreen mode
// Template
export function DashboardTemplate({ metrics, user }) {
  return (
    <main>
      <Navbar user={user} />
      <StatsCard title="Page Views" amount={metrics.pageView} />;
      <StatsCard title="Bounce Rate" amount={metrics.bounceRate} />;
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Jadi consume data eksternal hanya terjadi di dalam Page, sedangkan Template dan component di dalamnya cukup menerima data tersebut melalui props. Tidak ada component yang boleh memanggil useMetricsData atau pun useUserData selain Page.

Kelebihan

  • Seluruh component menjadi terisolasi dan reusable, karena tidak terikat dengan data eksternal tertentu
  • Lebih mudah untuk di-test, karena data pada component dapat diisi cukup menggunakan props, dan tidak perlu melakukan mocking atau membuat custom wrapper

Kekurangan

  • Terjadi prop drilling, yang berarti kita perlu menurunkan nilai tersebut ke component-nya satu per satu hingga component terdalam
  • Terjadi re-render pada seluruh component ketika data eksternal berubah

Consume langsung pada component, pindah ke luar jika diperlukan saja

Setelah mengalami kesengsaraan menurunkan nilai ke props pada component yang berlapis-lapis, dan ternyata juga cukup berisiko pada performa aplikasi, saya memutuskan untuk memindahkan consume data dari Root component ke component yang membutuhkannya langsung.

export function Dashboard() {
  // Consume di sini karena data `metrics` digunakan oleh lebih dari satu component
  const metrics = useMetricsData();

  return (
    <main>
      <Navbar />
      <StatsCard title="Page Views" amount={metrics.pageView} />;
      <StatsCard title="Bounce Rate" amount={metrics.bounceRate} />;
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode
export function Navbar() {
  // Consume langsung di sini karena pada Dashboard hanya component ini yang perlu data `user`
  const user = useUserData();

  return (
    <nav>
      Hello {user}!
    </nav>
  );
}
Enter fullscreen mode Exit fullscreen mode

Kelebihan

  • Tidak ada lagi prop drilling yang tidak diperlukan
  • Component tidak melakukan re-render yang tidak perlu

Kekurangan

  • Component tidak reusable secara default. Sehingga jika suatu saat component diperlukan untuk menampilkan data yang berbeda, maka akan perlu dilakukan refactoring
  • Component jadi lebih sulit untuk di-test

Setelah sekian lama menggunakan cara kedua, saya menemukan cara yang lebih baik:

Membuat wrapper untuk consume data eksternal

Cara ini saya temukan setelah merasakan pain point saat melakukan testing component; mocking data eksternal. Terlebih lagi pada masih fase awal pengembangan, yang dimana data eksternal ini masih sangat mudah berubah bentuk, sedangkan props component tidak.

Dari situ saya terpikir, component yang mau saya test saja tidak berubah, kenapa saya harus capek-capek revisi mock-nya?

Akhirnya saya memutuskan untuk memisahkan consume data eksternal ke dalam komponen tersendiri.

export function DashboardLoader() {
  const metrics = useMetricsData();

  return <Dashboard metrics={metrics} />
}
Enter fullscreen mode Exit fullscreen mode
import { NavbarLoader as Navbar } from './Navbar';

export function Dashboard({ metrics }) {
  return (
    <main>
      <Navbar />
      <StatsCard title="Page Views" amount={metrics.pageView} />;
      <StatsCard title="Bounce Rate" amount={metrics.bounceRate} />;
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode
export function NavbarLoader() {
  const user = useUserData();

  return <Navbar user={user} />
}
Enter fullscreen mode Exit fullscreen mode

Kelebihan

  • Seluruh component menjadi reusable
  • Component mudah di-test
  • Tidak terjadi prop drilling

Kekurangan

  • Menulis sedikit lebih banyak untuk membuat wrapper-nya
  • Pada kasus tertentu, terjadi rerender pada component yang tidak berkaitan. Seperti pada contoh di atas, Navbar akan rerender ketika metrics berubah

Begitulah alasan mengapa sebaiknya tidak menempatkan consume data eksternal, entah itu API call, global state, dlsb, pada component-nya langsung.

Namun, jika terdapat cara yang lebih baik, saya dengan senang hati untuk berubah pikiran, karena di balik programmer yang bahagia ada kode yang bersih.

Terima kasih sudah membaca.

Discussion

pic
Editor guide