Blazor dari Nol: Bab 3 — Komponen yang Skalabel

· 3 menit baca

Selamat datang di Bab 3 Blazor dari Nol. Kalau kamu belum membaca Bab 2, baca dulu agar proyek dasar kamu sudah siap.

Di Bab 2 kita membuat aplikasi berjalan. Di bab ini kita membuatnya mudah dipelihara.

Proyek Blazor cepat berantakan kalau setiap halaman jadi file .razor yang besar. Komponen membantu mencegah itu: konsistensi, reuse, dan batas yang jelas antar bagian UI.


Sebenarnya apa itu komponen Blazor?

Komponen adalah file .razor yang bisa:

  • Merender markup
  • Menyimpan state lokal
  • Menerima input lewat parameter
  • Mengirim event ke komponen parent
  • Merender konten child

Saat runtime, Blazor memperlakukan komponen sebagai mesin state kecil. Saat state berubah, Blazor re-render dan menerapkan diff DOM.

Jadi targetmu adalah input yang jelas dan perilaku yang bisa diprediksi.


Langkah 1: Mulai dari komponen yang fokus

Buat Components/Common/SectionHeader.razor:

<header class="section-header">
    <h2>@Title</h2>
    @if (!string.IsNullOrWhiteSpace(Subtitle))
    {
        <p>@Subtitle</p>
    }
</header>

@code {
    [Parameter] public string Title { get; set; } = string.Empty;
    [Parameter] public string? Subtitle { get; set; }
}

Pakai di Components/Pages/Home.razor:

@page "/"

<PageTitle>Blazor dari Nol</PageTitle>

<SectionHeader
    Title="Blazor dari Nol"
    Subtitle="Bab 3 membahas komponen." />

Contoh ini kecil, tapi idenya besar: komponen seharusnya mudah dipahami hanya dari parameternya.


Langkah 2: Gunakan parameter untuk perilaku yang eksplisit

Sekarang kita buat tombol reusable.

Components/Common/AppButton.razor:

<button class="app-button @VariantCssClass" @onclick="OnClick">
    @Text
</button>

@code {
    [Parameter] public string Text { get; set; } = "Button";
    [Parameter] public string Variant { get; set; } = "primary";
    [Parameter] public EventCallback OnClick { get; set; }

    private string VariantCssClass => Variant.ToLowerInvariant() switch
    {
        "secondary" => "app-button--secondary",
        "danger" => "app-button--danger",
        _ => "app-button--primary"
    };
}

Pemakaian:

@code {
    private int _savedCount;

    private void Save()
    {
        _savedCount++;
    }
}

<AppButton Text="Save" Variant="primary" OnClick="Save" />
<p>Tersimpan @_savedCount kali.</p>

Fokus utamanya adalah desain kontrak:

  • Parameter sedikit dan jelas
  • Nama eksplisit, bukan perilaku “ajaib”
  • Default yang aman

Langkah 3: Pakai RenderFragment untuk komposisi layout

RenderFragment memungkinkan parent mengirim blok UI ke child.

Buat Components/Common/Card.razor:

<article class="card">
    <header class="card__header">@Title</header>
    <section class="card__body">
        @ChildContent
    </section>
</article>

@code {
    [Parameter] public string Title { get; set; } = string.Empty;
    [Parameter] public RenderFragment? ChildContent { get; set; }
}

Pemakaian:

<Card Title="Roadmap">
    <ul>
        <li>Komponen</li>
        <li>Data binding</li>
        <li>Routing</li>
    </ul>
</Card>

Pola ini menjaga struktur halaman tetap konsisten tanpa mengulang wrapper markup.


Langkah 4: Utamakan komposisi dibanding halaman raksasa

Kalau halaman makin besar, pecah berdasarkan tanggung jawab:

  • ProfileSummary untuk blok identitas
  • ProfileStats untuk metrik
  • ProfileActivityList untuk aktivitas terbaru

Dengan begitu halaman jadi orchestration layer, bukan tempat semua detail dicampur.


Langkah 5: Seimbangkan markup dan logic

Untuk komponen sederhana, inline @code sudah cukup.

Untuk komponen besar, pindahkan logic ke code-behind:

  • UserCard.razor
  • UserCard.razor.cs

Ini membuat markup dan logic C# sama-sama lebih mudah dibaca.


Langkah 6: Struktur folder yang praktis

Struktur yang biasanya nyaman untuk scaling:

  • Components/Pages/ -> halaman yang punya route
  • Components/Layout/ -> app shell dan navigasi
  • Components/Common/ -> building block generik bersama
  • Components/Features/<FeatureName>/ -> komponen khusus fitur

Kesalahan umum di proyek Blazor awal

  • Terlalu banyak parameter daripada memakai view model khusus
  • Menaruh business rules langsung di page component
  • Membuat satu “God component” ratusan baris
  • Mengulang pola markup alih-alih ekstraksi komponen kecil

Bab berikutnya

Di Bab 4 kita fokus ke data binding dan events: @bind, event handling, trade-off two-way binding, dan pola state yang tetap predictable.