Memahami proses render pada React

12 Jul 2020 · 6 menit baca

Link berhasil di copy

“Programs must be written for people to read, and only incidentally for machines to execute.” ― Harold Abelson

Pengguna framework React pasti familiar dengan istilah render. Sebuah proses 'melukis' component React menjadi Document Object Model (DOM) yang dapat dipahami oleh browser lalu ditampilkan di layar.

Agar dapat lebih memahami apa itu render juga bagaimana sebuah komponen di-render ulang (re-render), sebaiknya kita memahami dulu bagaimana proses render terjadi, mulai dari awal.

Render

Document Object Model

Tampilan yang kita lihat pada sebuah website merupakan gabungan dari banyak 'cabang' DOM (DOM node). Pada tiap node terdapat representasi dari tiap elemen yang ada pada kode sumber HTML atau XML, seperti body, h1 atau blockquote.

DOM memiliki struktur seperti sebuah pohon yang memiliki banyak cabang. Pada bagian paling atas ada element utama yang menjadi ayah, bagi banyak cabang dibawahnya.

DOM

Adanya DOM membuat kita dapat memanipulasi tampilan juga data pada web dengan Javascript, tanpa perlu me-refresh halaman web. Fitur ini didapat bukan tanpa biaya. Saat kita memanipulasi sebuah node yang ada pada DOM, akan terjadi proses reflow dan repaint yang mempengaruhi hampir seluruh bagian website.

Reflow adalah proses saat browser melakukan kalkulasi ulang terhadap perubahan layout setelah terjadi manipulasi. Contoh saat kita merubah display suatu elemen dari block menjadi none. Hal ini akan berpengaruh pada ruang yang ada pada tampilan, sehingga memicu proses reflow untuk mengatur tempat elemen lainya paska perubahan.

Layout yang sudah diatur ulang selanjutnya dapat dilakukan proses repaint, alias menempatkan elemen yang ada, pada layout yang sudah tersedia. Proses reflow dan repaint ini terjadi minimal satu kali saat website pertama kali diakses [1].

Virtual DOM, Pelukis yang cerdas

Melakukan update pada DOM yang memiliki sedikit komponen UI bukan suatu masalah, browser pun pasti memiliki sistem optimasinya sendiri yang membuat proses ini cepat.

Bagaimanapun, jika terdapat ribuan komponen pada suatu halaman, proses reflow dan repaint pada DOM akan menuntut waktu lebih lama. Perlu dilakukan optimasi atau trik lain agar performa web tetap maksimal.

Disinilah konsep Virtual DOM muncul yang dianggap memiliki performa lebih baik dibanding DOM. Virtual DOM sebenarnya adalah sebuah object Javascript biasa, yang merepresentasikan suatu DOM. Misalkan kita memiliki node <div id="app"></div> pada DOM, maka kita dapat merepresentasikanya sebagai sebuah object seperti ini:

const vApp = {
  type: "div",
  props: {
    id: "app",
  },
};

Penamaan key yang ada dalam object bebas dilakukan seperti apapun. Tidak ada aturan khusus tentang bagaimanakah Virtual DOM seharusnya terlihat. Selama hal itu merepresentasikan sebuah DOM, maka itu adalah Virtual DOM. Jika kamu tertarik untuk membuat sebuah Virtual DOM sederhana, silahkan baca tutorial dari Jason Wu ini.

Kembali ke topik, mengapa Virtual DOM menjadi strategi agar proses perubahan DOM jadi lebih cepat. Ketika suatu component React di proses oleh fungsi ReactDOM.render(), sebuah copy atau snapshot berbentuk Virtual DOM akan dibuat dari component tersebut.

Setelahnya, component yang sudah dirubah tersebut, di-render atau diterjemahkan menjadi DOM dan dapat tampil di browser. Apabila terjadi perubahan state terhadap component lewat event yang terjadi di DOM. React akan membuat objek Virtual DOM lain yang merepresentasikan perubahan tersebut.

Misalkan diawal kita menampilkan sebuah list dengan dua buah item, React akan membuat snapshot-nya dalam bentuk Virtual DOM

// keadaan awal _component_, dalam bentuk Virtual DOM
const vUl = {
  type: "ul",
  props: { className: "list-style" },
  children: [
    { type: "li", children: ["number 1"] },
    { type: "li", children: ["number 2"] },
  ],
};

Setelahnya proses initial render dilakukan, component ditampilkan di browser. Lalu di dalam DOM terdapat sebuah aksi yang merubah state pada component, dan berpengaruh pada jumlah list yang ada. Disini, React akan membuat Virtual DOM baru berdasarkan kondisi setelah perubahan state.

// keadaan setelah penambahan data pada list
const vUl = {
  type: "ul",
  props: { className: "list-style" },
  children: [
    { type: "li", children: ["number 1"] },
    { type: "li", children: ["number 2"] },
    { type: "li", children: ["number 3"] },
  ],
};

Kedua object diatas kemudian akan dikomparasikan lewat proses diffing, untuk menetukan letak perbedaanya serta mengkalkulasi metode terbaik untuk merubah DOM.

Hasil dari diffing berisi instruksi bagaimana melakukan perubahan pada DOM, yang kemudian diimplementasikan secara efisien, dengan hanya merubah bagian yang diperlukan saja dalam proses Reconciliation.

reconciliation

Untuk mendapatkan gambaran, saya membuat simulasi dengan React untuk menampilkan list yang akan bertambah setiap satu detik. Saat kita melihat pada DOM, elemen li akan bertambah satu persatu, dan tidak terjadi repaint pada keseluruhan halaman di tiap perubahan.

Untuk melihat proses repaint yang terjadi, kamu bisa menggunakan mengaktifkan fitur paint flashing pada Chrome DevTools. Kerlip cahaya hijau akan menandai, bagian manakah yang ditambahkan pada DOM. Cobalah kode untuk gif diatas disini

Algoritma Diffing

re-render adalah proses yang akan terjadi ketika state atau props dalam suatu komponen mengalami perubahan. Terjadinya re-render tidak selalu merubah DOM yang ada. Perubahan pada DOM diatur oleh proses diffing, yang menentukan sejauh mana manipulasi pada DOM perlu terjadi.

Kita sudah hampir sampai di akhir pembahasan terkait render. Bagian ini hanya menyarikan apa yang tertulis pada dokumentasi terkait Reconciliation dan diffing di website ReactJS.

Ketika membangun ulang DOM setelah terjadi perubahan, ada beberapa skenario yang terjadi dalam diffing, untuk menentukan haruskah suatu DOM di buang (tear down) atau tidak.

  1. type berupa string, tidak mengalami perubahan, props pun tetap sama, maka DOM tidak akan berubah.
// sebelum dirubah
{ type: 'ul', props: { className: 'list-style' }}

// setelah dirubah
{ type: 'ul', props: { className: 'list-style' }}
  1. type tetap sama, tapi props berubah. React hanya akan merubah property yang berbeda, dan tidak menghapus node dari DOM.
// sebelum dirubah
{ type: 'ul', props: { className: 'list-style' }}

// setelah dirubah
{ type: 'ul', props: { className: 'list-style-special' }}
  1. type berubah dari string ke string atau ke component. Maka elemen lama akan dihilangkan (unmounted), diganti yang baru (mounting). Saat unmounting, component menerima lifecycle componentWillUnmount(), dan setelah component diganti baru, menerima lifecycle componentWillMount() lalu componentDidMount(). Semua state dari component lama akan hilang.
// sebelum dirubah
{ type: 'ul', props: { className: 'list-style' }}

// setelah dirubah
{ type: 'ul', props: { className: 'list-style-special' }}
// atau ...
{ type: SomeComponent, props: { className: 'list-style' }}
  1. Kedua type adalah component, props tetap sama. Jika type sama-sama merupakan referensi pada functional atau class component (component React pada umumnya). Maka React akan melihat kedalam masing-masing component dan melakukan perbandingan detail untuk menetukan perubahanya.
// sebelum dirubah
{ type: SebuahList, props: { className: 'list-style' }}

// setelah dirubah
{ type: SebuahList, props: { className: 'list-style-special' }}

React menggunakan dua istilah render, yaitu pada class render() dan ReactDOM.render(). render() berfungsi untuk merubah kode JSX menjadi elemen React (Virtual DOM), sedang ReactDOM.render() berfungsi untuk melakukan diffing hingga mounting component ke DOM.

Ada skenario lain terjadi ketika sebuah component memiliki lebih dari satu children.

// ...
children: [{ type: "div" }, { type: "span" }];
// ...
children: [
  { type: "div" },
  { type: "span" },
  { type: "p" }, // hanya menambah elemen ini saja ke DOM
];

Apabila terjadi penambahan children di akhir array, React hanya akan menambah satu node atau elemen baru di DOM. Namun jika penambahan terjadi diawal, maka algoritma diffing akan menganggapnya sebagai komponen yang berbeda, dan melakukan tear down pada semua child untuk membuat yang baru.

// ...
children: [{ type: "div" }, { type: "span" }];
// ...
children: [
  { type: "p" }, // penambahan yang merubah susunan
  { type: "div" },
  { type: "span" },
];

Hal ini menjadi tidak efisien secara performa, dan React dengan baik selalu mengingatkan kita ketika terjadi. Warning untuk memberikan key pada suatu iterasi komponen, berasal dari kerentanan terjadinya perubahan DOM menyeluruh pada children component.

Untuk mengatasinya, React dapat menerima atribut key pada children. Jika children memilikinya, maka React akan melakukan perbandingan pada key yang disediakan., bukan berdasar urutan awal children component.

Demikian adalah pembahasan terkait proses render pada framework React. Jika kamu memiliki suatu hal yang ingin didskusikan, jangan ragu untuk mengontakku via Twitter

Referensi

1. Optimizing react virtual dom explained

2. Understanding the virtual dom

Emot's Space © 2025