Gambaran Basis Kode

Bagian ini akan memberikan Anda gambaran mengenai penyusunan basis kode React, konvensi, serta implementasinya.

Jika Anda ingin berkontribusi pada React kami berharap panduan ini akan membantu Anda merasa lebih nyaman dalam membuat perubahan.

Kami tidak selalu merekomendasikan konvensi ini pada aplikasi React. Banyak di antaranya ada karena alasan historis dan mungkin berubah seiring berjalannya waktu.

Dependency Eksternal

React hampir tidak memiliki dependency eksternal. Biasanya, require() mengacu pada sebuah file pada basis kode React sendiri. Bagaimanapun, ada sedikit pengecualian yang relatif langka.

Repositori fbjs ada karena React berbagi beberapa utilitas dengan library seperti Relay, dan kami menjaga mereka tetap sinkron. Kami tidak bergantung pada modul-modul kecil yang ekuivalen pada ekosistem Node karena kami ingin engineer Facebook dapat mengubahnya kapanpun dibutuhkan. Tidak ada satupun utilitas dalam fbjs yang dianggap sebagai API publik, dan hanya ditujukan untuk penggunaan oleh proyek Facebook seperti React.

Folder Level Atas

Setelah melakukan kloning pada repositori React, Anda akan melihat beberapa folder teratas di dalamnya:

  • packages berisi metadata (seperti package.json) dan kode sumber (src subdirektori) untuk semua package pada repositori React. Jika perubahan Anda berkaitan dengan kode, subdirektori src dari setiap package adalah dimana Anda akan menghabiskan sebagian besar waktu Anda.
  • fixtures berisi beberapa aplikasi uji coba React untuk para kontributor.
  • build adalah output build dari React. Ia tidak ada pada repositori tetapi akan muncul pada hasil klon React Anda setelah Anda melakukan build untuk pertama kali.

Dokumentasi berada pada repositori terpisah dari React.

Ada beberapa folder teratas lainnya tetapi mereka sebagian besar digunakan untuk peralatan dan kemungkinan besar Anda tidak akan pernah menghadapi mereka ketika berkontribusi.

Tes Kolokasi

Kami tidak memiliki direktori level atas untuk tes unit. Sebagai gantinya, kami meletakkannya pada direktori bernama __tests__, relatif pada file yang ditesnya.

Misalnya, tes untuk setInnerHTML.js terletak tepat di sampingnya pada __tests__/setInnerHTML-test.js.

Peringatan dan Invariant

Basis kode React menggunakan modul warning untuk menampilkan peringatan:

var warning = require('warning');

warning(
  2 + 2 === 4,
  'Matematika tidak berfungsi hari ini.'
);

Peringatan ditampilkan ketika kondisi warning adalah false.

Satu cara yang baik untuk mengingatnya adalah bahwa kondisi seharusnya merefleksikan keadaan yang normal daripada yang tidak.

Ini adalah ide yang bagus untuk menghindari men-spam konsol dengan peringatan yang sama:

var warning = require('warning');

var didWarnAboutMath = false;
if (!didWarnAboutMath) {
  warning(
    2 + 2 === 4,
    'Matematika tidak berfungsi hari ini.'
  );
  didWarnAboutMath = true;
}

Peringatan hanya diaktifkan pada mode pengembangan. Pada mode produksi, mereka dilepas seluruhnya. Jika Anda perlu melarang beberapa jalur kode untuk dijalankan, gunakan modul invariant:

var invariant = require('invariant');

invariant(
  2 + 2 === 4,
  'Anda tidak boleh lewat!'
);

Invariant dilontarkan ketika kondisiinvariant adalah false.

Invariant” hanya cara lain untuk mengatakan “kondisi ini selalu benar”. Anda dapat menganggapnya sebagai membuat assertion.

Merupakan hal yang penting untuk menjaga perilaku versi pengembangan dan produksi tetap serupa, sehingga invariant dilontarkan baik di mode pengembangan dan produksi. Pesan eror secara otomatis diganti dengan kode eror pada mode produksi untuk menghindari mempengaruhi ukuran byte secara negatif.

Pengembangan dan Produksi

Anda dapat menggunakan variabel pseudo-global __DEV__ pada basis kode untuk menjaga blok kode yang ditujukan hanya pada mode pengembangan.

Variabel ini di-inline pada tahap kompilasi, dan berubah menjadi pengecekan process.env.NODE_ENV !== 'production' pada build CommonJS.

Untuk build yang berdiri sendiri, ia menjadi true pada build yang tidak di -minify, dan dilepas seluruhnya dengan menggunakan blok if yang ia jaga pada build yang di-minify.

if (__DEV__) {
  // Kode ini hanya akan berjalan di mode pengembangan.
}

Flow

Kami baru saja mulai memperkenalkan pengecekan Flow pada basis kode. File yang ditandai dengan anotasi @flow pada komentar lisensi header mengalami pengecekan tipe.

Kami menerima pull request menambahkan anotasi Flow pada kode yang sudah ada. Anotasi Flow terlihat seperti ini:

ReactRef.detachRefs = function(
  instance: ReactInstance,
  element: ReactElement | string | number | null | false,
): void {
  // ...
}

Bila mungkin, kode baru harus menggunakan anotasi Flow. Anda dapat menjalankan yarn flow secara lokal untuk mengecek kode Anda menggunakan Flow.

Injeksi Dinamis

React menggunakan injeksi dinamis pada sebagian modul. Walaupun selalu eksplisit, hal tersebut masih disayangkan karena menghalangi pemahaman terhadap kode. Alasan utamanya adalah karena React mulanya hanya mendukung DOM sebagai target. React Native dimulai sebagai fork dari React. Kami perlu menambahkan injeksi dinamis agar React Native dapat melakukan override pada beberapa perilaku.

Anda mungkin melihat modul yang mendeklarasikan dependency dinamis seperti ini:

// Diinjeksi secara dinamis
var textComponentClass = null;

// Bergantung pada nilai yang diinjeksi secara dinamis
function createInstanceForText(text) {
  return new textComponentClass(text);
}

var ReactHostComponent = {
  createInstanceForText,

  // Menyediakan kesempatan untuk injeksi dinamis
  injection: {
    injectTextComponentClass: function(componentClass) {
      textComponentClass = componentClass;
    },
  },
};

module.exports = ReactHostComponent;

Field injection tidak ditangani secara khusus dengan cara apapun. Tapi menurut konvensi, modul ini perlu beberapa dependency (agaknya spesifik pada platform tertentu) yang diinjeksi pada saat sedang berjalan (runtime).

Terdapat beberapa poin untuk injeksi pada basis kode. Di waktu yang akan datang, kami berniat untuk menghilangkan mekanisme injeksi dinamis dan menghubungkan semua bagian secara statis selama proses build.

Multiple Package

React adalah sebuah monorepo. Repositorinya berisi banyak package terpisah sehingga perubahan mereka dapat dikoordinasikan bersama, dan isu-isu berada pada satu tempat.

Inti React

Inti dari React berisi semua API level atas React, misalnya:

  • React.createElement()
  • React.Component
  • React.Children

Inti React hanya berisi API yang dibutuhkan untuk mendefinisikan komponen. Ia tidak termasuk algoritma reconciliation atau kode spesifik platform lainnya. Ia digunakan oleh React DOM dan komponen React Native.

Kode untuk inti React terletak di packages/react pada diagram sumber. Ia tersedia pada npm sebagai package react. Build browser yang berdiri sendiri disebut react.js, dan ia mengekspor sebuah global yang disebut React.

Renderer

React mulanya dibuat demi DOM tetapi kemudian diadaptasi untuk mendukung platform native dengan React Native. Hal ini memperkenalkan konsep renderer pada tim internal React.

Renderer mengatur bagaimana sebuah diagram React berubah menjadi panggilan platform yang mendasarinya

Renderer juga terletak pada packages/:

Satu-satunya Renderer lain yang didukung secara resmi adalah react-art. Ia dulu terletak pada repositori GitHub terpisah tetapi kami memindahkannya pada diagram sumber main sekarang.

Catatan:

Secara teknis react-native-renderer adalah sebuah lapisan yang sangat tipis yang mengajarkan React untuk berinteraksi dengan implementasi React Native. Kode spesifik platform yang sesungguhnya mengatur tampilan native yang hidup di dalam repositori React Native bersama dengan komponennya.

Reconciler

Bahkan renderer yang sangat berbeda seperti React DOM dan React Native perlu berbagi banyak logika. Khususnya, algoritma reconciliation harus semirip mungkin agar proses render deklaratif, komponen kustom, state, metode lifecycle, dan refs bekerja secara konsisten antar platform.

Untuk menyelesaikan hal ini, renderer berbeda berbagi beberapa kode di antara mereka. Kami menyebut bagian React ini sebuah ”reconciler”. Ketika sebuah pembaruan seperti setState() dijadwalkan, reconciler memanggil render() pada komponen di diagram, dan memasang, membarui, atau melepas mereka.

Reconciler tidak dipaketkan terpisah karena saat ini mereka tidak memiliki API publik. Sebagai gantinya, mereka digunakan secara eksklusif oleh renderer seperti React DOM dan React Native.

Reconciler Stack

Reconciler “stack” adalah implementasi yang menggerakan React 15 dan versi sebelumnya. Kami telah berhenti menggunakannya, tapi ia didokumentasikan dengan detil di bagian berikutnya

Reconciler Fiber

Reconciler “fiber” adalah usaha baru untuk menyelesaikan masalah yang melekat pada reconciler stack dan memperbaiki beberapa isu yang telah lama ada. Ia telah menjadi reconciler default sejak React 16.

Tujuan utamanya adalah:

  • Kemampuan untuk membagi tugas yang dapat diinterupsi menjadi potongan kecil.
  • Kemampuan untuk memprioritaskan, rebase dan menggunakan kembali tugas yang sedang berjalan.
  • Kemampuan untuk bolak-balik antara parent dan children untuk mendukung layout pada React.
  • Kemampuan untuk mengembalikan beberapa elemen dari render().
  • Dukungan yang lebih baik untuk batasan eror.

Anda dapat membaca lebih banyak mengenai Arsitektur React Fiber di sini dan di sini. Walaupun ia telah dikirimkan bersama dengan React 16, fitur-fitur async belum diaktifkan secara default.

Kode sumbernya terletak di packages/react-reconciler.

Sistem Event

React menerapkan sebuah sistem event sintetis yang agnostik terhadap renderer-nya dan bekerja dengan React DOM dan React Native. Kode sumbernya terletak di packages/react-events.

Terdapat sebuah video dengan pembahasan mendalam mengenai kodenya (66 menit).

Selanjutnya Apa?

Bacalah bagian berikutnya untuk mempelajari mengenai implementasi reconciler sebelum React 16 secara lebih detil. Kami belum mendokumentasikan bagian internal dari reconciler yang baru.