Terakhir diperbarui: 2024-03-26

Penulis: Tim PBF 2024

Context di ReactJS dengan Next.js

Pada codelab ini Anda akan mempelajari tentang penggunaan Context di ReactJS dengan Next.js.

Pengetahun yang Anda harus miliki

Sebelum memulai codelab ini, sebaiknya Anda memiliki pengetahuan dasar tentang:

Apa yang Anda akan pelajari

Apa yang Anda perlu persiapkan

Apa itu Context ?

Di ReactJS, context merupakan fitur yang menyediakan cara untuk mengirim data melalui pohon komponen tanpa harus melalui props secara manual pada setiap level atau tingkatan.

Biasanya aplikasi React mengirim data dengan formasi top-down (parent ke child) melalui props, tetapi penggunaannya dapat menjadi rumit untuk beberapa jenis props (seperti pengaturan bahasa, tema UI) yang melibatkan banyak komponen dalam sebuah aplikasi. Context menyediakan cara untuk berbagi value antar komponen tanpa melalui props pada setiap level dari komponen.

Mengoper props bisa menjadi bertele-tele dan merepotkan jika Anda harus mengopernya melalui banyak komponen di tengah-tengah, atau jika banyak komponen di aplikasi Anda membutuhkan informasi yang sama. Context memungkinkan komponen induk untuk membuat beberapa informasi tersedia di komponen lain dalam sebuah pohon (tree) di bawahnya—tidak peduli seberapa dalam—tanpa mengopernya secara eksplisit melalui props.

Kapan menggunakan Context ?

Context dirancang untuk berbagi data secara "global" ke berbagai komponen React, seperti data autentikasi pengguna yang sedang aktif, tema, atau pengaturan bahasa.

Perhatikan, Sebelum Anda menggunakan Context

Context terutama digunakan ketika beberapa data dibutuhkan untuk diakses oleh banyak komponen pada setiap tingkatan (nesting levels). Gunakanlah dengan bijak karena itu akan mempersulit penggunaan kembali (reuse) komponen yang ada.

Masalah ketika mengoper props

Mengoper props adalah cara yang bagus untuk menyalurkan data secara eksplisit melalui pohon (tree) UI Anda ke komponen yang menggunakanya.

Tapi mengoper props bisa menjadi bertele-tele dan tidak nyaman ketika Anda perlu mengoper beberapa prop secara mendalam melalui pohon (tree), atau jika banyak komponen membutuhkan prop yang sama. Bisa jadi jauh dari komponen yang membutuhkan data, dan memindahkan state ke atas dapat menyebabkan yang disebut "prop drilling".

Bukankah lebih bagus jika ada cara untuk "memindahkan" data ke komponen di dalam pohon (tree) yang membutuhkannya tanpa harus mengoper props? Dengan fitur context React, ternyata ada!

Pada praktikum ini, Anda akan mempelajari cara menggunakan context dengan diakses oleh komponen child didalamnya. Context memungkinkan sebuah komponen induk menyediakan data untuk seluruh pohon (tree) di bawahnya. Ada banyak kegunaan dari context. Berikut ini salah satu contohnya. Silakan lakukan langkah-langkah praktikum berikut ini.

Langkah 1: Buat project baru dan repo baru di GitHub

Silakan buat project baru seperti berikut dan repo baru dengan nama #07-belajar-context-nextjs

Langkah 2: Buat struktur folder dengan prinsip atomic design

Buatlah folder baru di src/components seperti berikut ini

Langkah 3: Buat komponen atom baru

Buat file baru di src/components/atoms/heading.tsx berisi kode sebagai berikut.

export default function Heading({ level, children }: { level: number; children: any }) {
    switch (level) {
        case 1:
            return <h1>{children}</h1>;
        case 2:
            return <h2>{children}</h2>;
        case 3:
            return <h3>{children}</h3>;
        case 4:
            return <h4>{children}</h4>;
        case 5:
            return <h5>{children}</h5>;
        case 6:
            return <h6>{children}</h6>;
        default:
            throw Error('Unknown level: ' + level);
    }
}

Kemudian buat file baru di src\components\atoms\section.tsx berisi kode berikut.

export default function Section({ children }: { children: any }) {
    return (
        <section className="section">
            {children}
        </section>
    );
}

Lalu bagian MainPage buat file baru di src\components\templates\main_page.tsx berisi kode sebagai berikut.

import Heading from "../atoms/heading";
import Section from "../atoms/section";

export default function MainPage() {
    return (
        <Section>
            <Heading level={1}>Title</Heading>
            <Section>
                <Heading level={2}>Heading</Heading>
                <Heading level={2}>Heading</Heading>
                <Heading level={2}>Heading</Heading>
                <Section>
                    <Heading level={3}>Sub-heading</Heading>
                    <Heading level={3}>Sub-heading</Heading>
                    <Heading level={3}>Sub-heading</Heading>
                    <Section>
                        <Heading level={4}>Sub-sub-heading</Heading>
                        <Heading level={4}>Sub-sub-heading</Heading>
                        <Heading level={4}>Sub-sub-heading</Heading>
                    </Section>
                </Section>
            </Section>
        </Section>
    );
}

Perhatikan komponen Heading tersebut yang menerima level untuk ukurannya.

Langkah 4: Ubah isi kode page.tsx dan run

Ubahlah kode di src\app\page.tsx seperti berikut. Lalu run dan lihat hasilnya di browser Anda.

Katakanlah Anda ingin beberapa judul dalam Section yang sama selalu memiliki ukuran yang sama.

Saat ini, Anda mengoper level props ke tiap < Heading > secara terpisah:

Akan lebih baik jika Anda dapat mengoper prop level ke komponen < Section > dan menghapusnya dari komponen < Heading > Dengan cara ini Anda dapat menerapkan bahwa semua judul di bagian yang sama memiliki ukuran yang sama:

Tapi bagaimana komponen < Heading > dapat mengetahui level < Section > yang terdekat? Hal ini akan membutuhkan suatu cara untuk "meminta" data dari suatu tempat di atas pohon (tree).

Anda tidak bisa melakukannya dengan props sendirian. Di sinilah context berperan penting. Anda akan melakukannya dalam tiga langkah:

  1. Buat sebuah context. (Anda dapat menamainya LevelContext, karena ini untuk level judul.)
  2. Gunakan context tersebut dari komponen yang membutuhkan data. (Heading akan menggunakan LevelContext.)
  3. Sediakan context tersebut dari komponen yang menentukan data. (Section akan menyediakan LevelContext.)

Context memungkinkan sebuah induk—bahkan yang jauh sekalipun!—menyediakan beberapa data kepada seluruh komponen pohon (tree) di dalamnya.

Langkah 5.1: Buat Context

Pertama, Anda perlu membuat context. Anda harus mengekspornya dari sebuah file sehingga komponen Anda dapat menggunakannya. Buatlah file baru di src\utilities\context\mycontext.tsx yang berisi kode sebagai berikut:

Satu-satunya argumen untuk createContext adalah nilai default. Disini, 1 merujuk pada level heading terbesar, tapi Anda dapat mengoper nilai apa pun (bahkan sebuah objek). Anda akan melihat pentingnya nilai default di langkah selanjutnya.

Langkah 5.2: Gunakan context

Ubahlah isi kode komponen Heading dengan Impor useContext Hook dari React dan context Anda:

Saat ini, komponen Heading membaca level dari props. Sebagai gantinya, hapus prop level dan baca nilai dari context yang baru saja Anda impor, LevelContext:

useContext adalah sebuah Hook. Sama seperti useState dan useReducer, Anda hanya dapat memanggil sebuah Hook secara langsung di dalam komponen React (bukan di dalam pengulangan atau pengkondisian). useContext memberitahu React bahwa komponen Heading mau membaca LevelContext.

Sekarang komponen Heading tidak membutuhkan sebuah prop level, Anda tidak perlu mengoper level prop ke Heading di JSX Anda. Sebagai gantinya Perbarui JSX sehingga Section yang dapat menerimanya:

Perhatikan contoh ini masih belum berfungsi dengan baik! Semua judul memiliki ukuran yang sama karena meskipun Anda menggunakan context, Anda belum menyediakannya. React tidak tahu darimana untuk mendapatkannya!

Jika Anda tidak menyediakan context, React akan menggunakan nilai default yang sudah Anda tentukan di langkah sebelumnya. Di contoh ini, Anda menentukan 1 sebagai argumen createContext, jadi useContext(LevelContext) mengembalikan 1, mengatur semua headings ke < h1 >. Ayo kita perbaiki masalah ini dengan membuat setiap Section menyediakan context-nya sendiri.

Langkah 5.3: Sediakan context

Komponen Section saat ini merenders anaknya, bungkus mereka semua dengan sebuah context provider untuk menyediakan LevelContext kepada mereka seperti kode berikut:

Ini memberitahu React: "jika ada komponen di dalam < Section > ini yang meminta LevelContext, berikan level ini." Komponen akan menggunakan nilai dari < LevelContext.Provider > terdekat di pohon UI (tree) di atasnya.

Hasilnya sama dengan kode aslinya, tapi Anda tidak perlu mengoper prop level ke setiap komponen Heading! Sebagai gantinya, ia "mencari tahu" level heading-nya dengan meminta Section terdekat di atasnya:

  1. Anda mengoper prop level ke < Section >.
  2. Section membungkus anaknya dengan < LevelContext.Provider value={level} >.
  3. Heading meminta nilai terdekat dari LevelContext di atasnya dengan useContext(LevelContext).

Langkah 6: Menggunakan dan menyediakan context dari komponen yang sama

Saat ini, Anda masih harus menentukan setiap level section secara manual, karena context memungkinan Anda membaca informasi dari komponen di atasnya, setiap Section dapat membaca level dari Section di atasnya, dan mengoper level + 1 ke bawah secara otomatis. Berikut adalah bagaimana Anda dapat melakukannya dengan mengubah sedikit kode pada komponen Section:

Dengan perubahan ini, Anda tidak perlu mengoper prop level baik ke < Section > atau ke < Heading >:

Sekarang keduanya Heading dan Section membaca LevelContext untuk mencari tahu seberapa "dalam" mereka. Dan Section membungkus anaknya ke dalam LevelContext untuk menentukan bahwa apa pun yang ada di dalamnya berada pada level yang "lebih dalam".

Anda dapat menyisipkan sebanyak mungkin komponen di antara komponen yang menyediakan context dan komponen yang menggunakannya. Ini termasuk komponen bawaan seperti < div > dan komponen yang mungkin Anda buat sendiri.

Di praktikum berikut, komponen Post yang sama (dengan batas putus-putus) diberikan pada dua tingkat sarang yang berbeda. Perhatikan bahwa < Heading > di dalamnya mendapatkan level-nya secara otomatis dari < Section > terdekat.

Anda tidak perlu melakukan sesuatu yang khusus untuk praktikum ini. Section menentukan context untuk pohon (tree) di dalamnya, jadi Anda dapat menyisipkan < Heading > di mana saja, dan akan memiliki ukuran yang benar.

Langkah 1: Buat komponen atom baru

Buatlah file baru di src\components\atoms\section2.tsx berisi kode sebagai berikut.

Lalu buatlah file baru di src\components\atoms\post.tsx dengan kode berikut.

Selanjutnya kita buat molecules di src\components\molecules\recentpost.tsx dengan kode berikut.

Kemudian buat organisms di src\components\organisms\allpost.tsx dengan kode berikut.

Terakhir kita buat templates di src\components\templates\profile_page.tsx dengan kode berikut.

Langkah 2: Tambahkan ProfilePage ke page.tsx lalu run

Tambahkan komponen ProfilePage seperti kode berikut.

Hapus bagian theme pada file tailwind.config.ts seperti kode berikut.

Hapus semua style CSS di file src\app\globals.css lalu ganti dengan kode berikut.

Ketika Anda run dengan npm run dev maka di browser akan tampil seperti berikut.

Pada praktikum kali ini melanjutkan project dari praktikum sebelumnya, Anda akan membuat tema web yang bisa diubah menjadi mode light atau dark. Tampilan aplikasi web seperti berikut ini (Anda dapat berkreasi dengan konten dan style yang lain sesuai selera Anda).

Langkah 1: Membuat variabel tema

Buatlah file dan folder baru di src\utilities\themes\mythemes.tsx yang berisi kode berikut.

Kemudian buatlah contextnya di file src\utilities\contexts\mycontext.tsx

Langkah 2: Buat komponen atom NavBar

Buatlah file baru di src\components\atoms\navbar.tsx

Langkah 3: Buat Provider

Buatlah provider di src\components\atoms\myapp.tsx

Langkah 4: Buat masing-masing page

Pindahkan komponen ProfilePage ke file src\components\templates\profile_page.tsx

Sehingga struktur folder di templates menjadi seperti berikut. Anda dapat berkreasi dengan konten pada About dan Contacts.

Langkah 5: Buat routing

Buatlah folder dan file baru di dalam app agar routing page masing-masing dapat diakses seperti berikut.

Gantilah isi kode pada src\app\page.tsx menjadi seperti berikut.

Termasuk di masing-masing page src\app\profile\page.tsx, untuk page About dan Contacs silakan Anda sesuaikan.

Context memungkinkan Anda untuk menulis komponen yang "beradaptasi dengan sekitar mereka" dan menampilkan diri mereka secara berbeda tergantung di mana (atau, dalam kata lain, dalam context apa) mereka akan diberikan.

Cara kerja context mungkin mengingatkan Anda pada pewarisan properti CSS. Pada CSS, Anda dapat menentukan color: blue untuk < div >, dan simpul DOM apa pun di dalamnya, tidak peduli seberapa dalam, akan mewarisi warna tersebut kecuali ada simpul DOM lain di tengahnya yang menimpanya dengan color: green. Demikian pula, dalam React, satu-satunya cara untuk menimpa beberapa context yang berasal dari atas adalah dengan membungkus anaknya ke dalam penyedia context dengan nilai yang berbeda.

Pada CSS, properti berbeda seperti color dan background-color tidak akan menimpa satu sama lain. Anda dapat mengatur semua color < div > ke merah tanpa berdampak pada background-color. Demikian pula, React contexts yang berbeda tidak akan menimpa satu sama lain. Tiap context yang Anda buat dengan createContext() benar-benar terpisah dari yang lain, dan menyatukan komponen-komponen yang menggunakan dan menyediakan context tertentu. Satu komponen dapat menggunakan atau menyediakan banyak context yang berbeda tanpa masalah.

Pertimbangkan sebelum Anda menggunakan Context

Context sangat menggoda untuk digunakan. Namun, ini juga terlalu mudah untuk menggunakannya secara berlebihan. Hanya karena Anda membutuhkan untuk mengoper beberapa props beberapa tingkat lebih dalam bukan berarti Anda memasukkan informasi tersebut ke dalam context.

Ini adalah beberapa alternatif yang harus Anda pertimbangkan sebelum menggunakan context:

  1. Mulai dengan mengoper props. Jika komponen Anda tidak sepele, bukan hal yang aneh jika Anda mengoper selusin props melalui selusin komponen. Ini mungkin terasa seperti pekerjaan berat, tapi membuatnya sangat jelas komponen yang mana menggunakan data yang mana! Orang yang mengelola kode Anda akan senang Anda telah membuat aliran data eksplisit dengan props.
  2. Ekstrak komponen dan oper JSX sebagai children. Jika Anda mengoper beberapa data melewati banyak lapisan komponen perantara yang tidak menggunakan data tersebut (dan hanya mengopernya lebih jauh ke bawah), ini sering kali Anda lupa mengekstrak beberapa komponen di sepanjang jalan. Contohnya, mungkin Anda mengoper data props seperti posts ke komponen visual yang tidak menggunakannya secara langsung, seperti < Layout posts={posts} / >. Sebagai gantinya, buat Layout mengambil children sebagai prop, dan berikan < Layout > < Posts posts={posts} / >< / Layout >. Hal ini mengurangi jumlah lapisan antara komponen yang menentukan data dan komponen yang membutuhkannya.

Jika tidak satu pun dari pendekatan ini yang cocok untuk Anda, pertimbangkan dalam menggunakan context.

Kapan menggunakan context yang cocok?

Context tidak terbatas pada nilai statis. Jika anda memberikan nilai yang berbeda pada render berikutnya, React akan memperbarui semua komponen yang membacanya di bawahnya! Inilah sebabnya mengapa context sering digunakan bersama degan state.

Pada umumnya, jika beberapa informasi dibutuhkan oleh komponen yang jauh di beberapa bagian pohon (tree), ini adalah indikasi yang bagus bahwa context akan membantu Anda.

  1. Buat dan ekspor ia dengan export const MyContext = createContext(defaultValue).
  2. Oper ke useContext(MyContext) Hook untuk membacanya di komponen anak manapun, tidak peduli seberapa dalam.
  3. Bungkus anak ke < MyContext.Provider value={...} > untuk menyediakannya dari induk.

Selamat, Anda telah berhasil menyelesaikan codelab ini. Semoga mendapatkan ilmu yang bermanfaat.

Apa selanjutnya?

Silakan cek beberapa sumber belajar lainnya...

Referensi