Rules of Hooks

Hooks adalah sebuah fitur tambahan baru di React 16.8. Mereka membantu Anda untuk menggunakan state and fitur-fitur React lainnya tanpa menulis sebuah kelas.

Hooks adalah fungsi JavaScript, tetapi Anda perlu mengikuti dua aturan pada saat menggunakannya. Kami menyediakan sebuah linter plugin untuk memberlakukan aturan-aturan ini secara otomatis:

Hanya Panggil Hooks di Tingkat Atas

Jangan memanggil Hooks dari dalam loops, conditions, atau nested functions. Melainkan, selalu gunakan Hooks di tingkat atas dari fungsi React Anda. Dengan mengikuti aturan ini, Anda dapat dengan yakin dapat memastikan bahwa Hooks akan dipanggil dengan urutan yang sama setiap kali sebuah komponen me-render. Hal itu yang menyebabkan React dapat menyimpan state dari Hooks dengan benar di antara banyak panggilan useState dan useEffect. (Jika Anda ingin tahu lebih lanjut, kami akan menjelaskan ini lebih dalam di bawah.)

Hanya Panggil Hooks dari Fungsi-Fungsi React

Jangan memanggil Hooks dari fungsi-fungsi JavaScript biasa. Sebagai gantinya, Anda dapat:

  • ✅ Memanggil Hooks dari komponen-komponen fungsi React.
  • ✅ Memanggil Hooks dari custom Hooks (kita akan belajar tentang ini di laman berikutnya).

Dengan mengikuti aturan ini, Anda dapat dengan yakin memastikan bahwa semua logika stateful di dalam sebuah komponen terlihat jelas dari source code-nya.

Plugin ESLint

Kami membuat sebuah ESLint plugin dengan nama eslint-plugin-react-hooks yang membantu menekankan dua aturan-aturan ini. Anda dapat menambahkan plugin ini ke proyek Anda jika Anda ingin mencobanya:

npm install eslint-plugin-react-hooks --save-dev
// Konfigurasi ESLint Anda
{
  "plugins": [
    // ...
    "react-hooks"
  ],
  "rules": {
    // ...
    "react-hooks/rules-of-hooks": "error", // Checks rules of Hooks
    "react-hooks/exhaustive-deps": "warn" // Checks effect dependencies
  }
}

Di masa mendatang, kami merencanakan untuk menyertakan plugin ini secara umum ke dalam Create React App dan toolkit-tookit sejenis.

Anda dapat melompat ke laman selanjutnya yang menjelaskan bagaimana membuat Hooks Anda sendiri sekarang. Di laman ini, kita akan melanjutkan dengan menjelaskan pemikiran dibalik aturan-aturan ini.

Penjelasan

Seperti yang telah dipelajari sebelumnya, kita dapat menggunakan banyak State atau Effect Hooks di dalam sebuah komponen:

function Form() {
  // 1. Menggunakan state variabel name
  const [name, setName] = useState('Mary');

  // 2. Menggunakan sebuah effect untuk mengukuhkan form
  useEffect(function persistForm() {
    localStorage.setItem('formData', name);
  });

  // 3. Menggunakan state variabel surname
  const [surname, setSurname] = useState('Poppins');

  // 4. Menggunakan sebuah effect untuk meng-update title
  useEffect(function updateTitle() {
    document.title = name + ' ' + surname;
  });

  // ...
}

Bagaimana React mengetahui state mana yang sesuai dengan panggilan useState yang mana? Jawabannya adalah React bergantung pada urutan bagaimana Hooks dipanggil. Contoh kami berjalan dengan baik karena urutan dari panggilan-panggilan Hook sama setiap kali render:

// ------------
// Render pertama
// ------------
useState('Mary')           // 1. Inisialisasi state variabel name dengan 'Mary'
useEffect(persistForm)     // 2. Tambahkan sebuah effect untuk mengukuhkan form
useState('Poppins')        // 3. Inisialisasi state variabel surname dengan 'Poppins'
useEffect(updateTitle)     // 4. Tambahkan sebuah effect untuk meng-update title

// -------------
// Render kedua
// -------------
useState('Mary')           // 1. Baca state variabel name (argument diabaikan)
useEffect(persistForm)     // 2. Ganti effect untuk mengukuhkan form
useState('Poppins')        // 3. Baca state variabel surname (argument diabaikan)
useEffect(updateTitle)     // 4. Ganti effect untuk meng-update title

// ...

Selama urutan panggilan-panggilan Hook sama dalam setiap render, React dapat mengasosiasikan beberapa state lokal dengannya. Tetapi apa yang akan terjadi jika menaruh sebuah panggilan Hook (contoh, effect persistForm) di dalam sebuah condition?

  // 🔴 Kita melanggar aturan pertama dengan menggunakan Hook di dalam sebuah condition
  if (name !== '') {
    useEffect(function persistForm() {
      localStorage.setItem('formData', name);
    });
  }

Condition name !== '' adalah true pada render pertama, jadi kita menjalankan Hook ini. Akan tetapi, pada render selanjutnya pengguna mungkin mengosongkan form, mengakibatkan condition menjadi false. Sekarang kita melangkahi Hook ini pada saat rendering, urutan panggilan-panggilan Hook menjadi tidak sama:

useState('Mary')           // 1. Baca state variabel name (argument diabaikan)
// useEffect(persistForm)  // 🔴 Hook ini dilangkahi!
useState('Poppins')        // 🔴 2 (sebelumnya 3). Gagal membaca state variabel surname
useEffect(updateTitle)     // 🔴 3 (sebelumnya 4). Gagal mengganti effect

React tidak akan mengetahui apa yang harus dikembalikan pada saat panggilan Hook useState kedua. React mengharapkan bahwa panggilan Hook kedua di komponen ini sesuai dengan effect persistForm, seperti render sebelumnya, tetapi tidak lagi. Sejak saat itu, setiap panggilan Hook selanjutnya setelah yang kita langkahi juga akan bergeser satu, mengakibatkan bugs.

Ini sebabnya Hooks harus dipanggil dari level atas komponen-komponen kita. Jika kita ingin menjalankan sebuah effect secara conditional, kita dapat menaruh condition tersebut di dalam Hook kita:

  useEffect(function persistForm() {
    // 👍 Kita tidak lagi melanggar aturan pertama
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

Catatan Anda tidak perlu khawatir tentang masalah ini lagi jika Anda menggunakan lint rule yang diberikan. Tetapi sekarang Anda juga mengetahui mengapa Hooks bekerja dengan cara ini, dan masalah apa yang dihindari dengan mengikuti aturan-aturan ini.

Langkah-Langkah Selanjutnya

Akhirnya, kita siap untuk belajar mengenai membuat Hook Anda sendiri! Custom Hooks memperbolehkan Anda untuk menggabungkan Hooks dari React ke dalam abstraksi milik Anda, dan menggunakan kembali logika stateful umum dalam komponen-komponen yang berbeda.