React Hooks — apa itu, hooks dasar dan aturan penggunaan

Penulis: IT Sectr Diterbitkan: 2026-07-04 Waktu membaca: 10 mnt

React Hooks — adalah fungsi yang memungkinkan penggunaan state, efek, konteks, dan kemampuan React lainnya dalam komponen fungsional, tanpa menulis kelas. Diperkenalkan di React 16.8, hooks secara radikal mengubah pendekatan pengembangan, menggantikan pola kompleks seperti HOC dan render props. Menurut React, 2024, ada 15 hooks bawaan yang mencakup semua skenario pengelolaan komponen.

Utama

  • Hooks — fungsi untuk menghubungkan kemampuan React ke komponen fungsional tanpa extends Component.
  • useState — hook dasar untuk mengelola state, mengembalikan nilai dan setter.
  • useEffect — untuk efek samping: permintaan API, langganan, timer, manipulasi DOM.
  • useContext — untuk akses ke konteks React tanpa pembungkus Consumer dan static contextType.
  • Aturan hooks — panggil hanya di tingkat atas dan hanya di dalam komponen fungsional atau hooks kustom.

Apa itu React Hooks?

React Hooks — sekumpulan fungsi bawaan di React yang memungkinkan ‘terhubung’ ke mekanisme internal framework tanpa membuat kelas. Sebelum React 16.8, komponen fungsional hanya bisa menerima props dan mengembalikan JSX — logika kompleks apa pun memerlukan komponen kelas.

Masalah komponen kelas — pemisahan logika terkait. Kode yang termasuk dalam satu entitas (misalnya, langganan WebSocket) terpaksa dibagi antara componentDidMount, componentDidUpdate, dan componentWillUnmount. Hooks menyelesaikan ini: semua logika dikelompokkan dalam satu useEffect.

Masalah kedua — penggunaan kembali logika. Untuk menggunakan kembali logika state antar komponen kelas, harus diterapkan HOC (komponen tingkat tinggi) atau render props — kedua pola menciptakan sarang tambahan di pohon komponen. Hooks kustom menyelesaikan masalah ini tanpa sarang.

useState — mengelola state

useState — hook paling dasar. Ia menerima nilai awal dan mengembalikan tuple: nilai saat ini dan fungsi untuk memperbaruinya. Berbeda dengan this.setState di kelas, useState tidak menggabungkan objek — state baru sepenuhnya menggantikan state sebelumnya untuk pemanggilan useState tertentu.

js
import React, { useState } from 'react';

function Form() {
  const [text, setText] = useState('');
  const [submitting, setSubmitting] = useState(false);

  async function handleSubmit() {
    setSubmitting(true);
    await fetch('/api/submit', {
      method: 'POST',
      body: JSON.stringify({ text })
    });
    setSubmitting(false);
  }

  return (
    <div>
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
      <button onClick={handleSubmit}
        disabled={submitting}>
        {submitting ? 'Saving...' : 'Submit'}
      </button>
    </div>
  );
}

Bentuk fungsional setState

Setter useState dapat menerima fungsi yang menerima nilai sebelumnya dan mengembalikan nilai baru. Bentuk fungsional wajib ketika state baru dihitung dari state sebelumnya dan pemanggilan setState secara batch dimungkinkan — setiap pemanggilan akan mendapatkan nilai sebelumnya yang terkini.

useEffect — efek samping dan siklus hidup

useEffect — hook untuk melakukan efek samping di komponen fungsional. Ia menerima dua argumen: fungsi efek dan array dependensi. React menjalankan efek setelah setiap render, tetapi dengan menentukan dependensi — hanya ketika salah satu dari mereka berubah.

Array dependensi kosong [] berarti efek dijalankan sekali setelah render pertama (analog dengan componentDidMount). Mengembalikan fungsi dari efek adalah pembersihan (cleanup), yang dijalankan saat pelepasan (analog dengan componentWillUnmount) dan sebelum eksekusi ulang efek.

js
import React, { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    const socket = new WebSocket(`wss://chat/${roomId}`);

    socket.onmessage = (event) => {
      setMessages(prev => [...prev, JSON.parse(event.data)]);
    };

    // Cleanup - tutup soket
    return () => socket.close();
  }, [roomId]);

  return <ul>
    {messages.map((msg, i) =>
      <li key={i}>{msg.text}</li>
    )}
  </ul>;
}

Dependensi useEffect — adalah satu-satunya hubungan antara efek dan dunia luar. Jika dalam efek digunakan variabel dari props atau state, ia harus ada di array dependensi. Mengabaikan aturan ini adalah sumber bug dengan data usang (stale closure).

useContext — akses ke konteks

useContext — hook untuk membaca nilai konteks. Ia menerima objek konteks yang dibuat dengan React.createContext dan mengembalikan nilai saat ini. Jika konteks berubah, komponen secara otomatis di-render ulang dengan nilai baru.

Sebelum munculnya useContext, akses ke konteks di komponen fungsional memerlukan pembungkusan dalam Context.Consumer, yang menciptakan sarang. useContext sepenuhnya menghilangkan Consumer — cukup panggil hook di mana saja di komponen fungsional.

js
import React, { createContext, useContext } from 'react';

const ThemeContext = createContext('terang');

function ThemedButton() {
  const theme = useContext(ThemeContext);

  return <button
    className={`btn btn-${theme}`}
  >
    {theme === 'dark' ? 'moon' : '☀️'}
  </button>;
}

useContext bukan pengganti Redux atau state global. React merekomendasikan penggunaan konteks untuk nilai yang jarang berubah — tema, bahasa, otorisasi. Untuk data yang sering diperbarui, setiap render ulang akan menghitung ulang semua konsumen konteks, yang dapat menurunkan kinerja.

useReducer — logika state kompleks

useReducer — alternatif untuk useState untuk state dengan logika pembaruan yang kompleks. Ia menerima fungsi reducer dan state awal, mengembalikan state saat ini dan fungsi dispatch. Reducer menerima state saat ini dan aksi, mengembalikan state baru — fungsi murni tanpa efek samping.

useReducer sangat berguna ketika beberapa aksi (set, reset, increment, addItem) mengelola satu state. Alih-alih beberapa useState dengan setter berbeda — satu reducer dengan tipe aksi yang jelas. Ini membuat logika dapat diprediksi dan mudah diuji.

js
import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { ...state, count: state.count + 1 };
    case 'setStep':
      return { ...state, step: action.step };
    case 'reset':
      return { count: 0, step: 1 };
    default:
      throw new Error('Unknown action: ' + action.type);
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, {
    count: 0,
    step: 1
  });

  return (
    <>
      <p>Count: {state.count}</p>
      <button
        onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button
        onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </>
  );
}

useRef, useMemo, useCallback — optimasi

useRef membuat objek yang dapat diubah yang bertahan di antara render. Mengubah current tidak menyebabkan render ulang. Penggunaan utama — akses ke elemen DOM dan penyimpanan nilai yang dapat diubah (timer, nilai sebelumnya, instance pustaka).

useMemo memoisasi hasil fungsi. Ia menghitung ulang nilai hanya ketika salah satu dependensi berubah. Gunakan untuk perhitungan mahal — memfilter array besar, sorting, memformat data. useCallback — kasus khusus useMemo untuk memoisasi fungsi.

js
import React, { useRef, useMemo, useCallback } from 'react';

function SearchPage({ items, query, onItemSelect }) {
  const inputRef = useRef(null);

  // Fokus otomatis saat mount
  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  // Memo hasil filter
  const filtered = useMemo(() => {
    return items.filter(item =>
      item.name.toLowerCase()
        .includes(query.toLowerCase())
    );
  }, [items, query]);

  // Memo callback
  const handleSelect = useCallback((id) => {
    onItemSelect(id);
  }, [onItemSelect]);

  return (
    <div>
      <input ref={inputRef}
        value={query}
        onChange={e => setQuery(e.target.value)}
      />
      <ul>
        {filtered.map(item =>
          <li key={item.id}>
            <button
              onClick={() => handleSelect(item.id)}>
              {item.name}
            </button>
          </li>
        )}
      </ul>
    </div>
  );
}

Aturan penting: useRef, useMemo, dan useCallback — alat optimasi, bukan konstruksi wajib. Jangan bungkus semuanya dalam useMemo — ukur kinerja terlebih dahulu. Optimasi prematur mempersulit kode. React merender komponen dengan cepat; useMemo hanya diperlukan ketika perhitungan benar-benar mahal (O(n²) dan lebih tinggi).

Aturan hooks dan kesalahan umum

React memperkenalkan dua aturan ketat untuk hooks. Pertama: panggil hooks hanya di tingkat atas — tidak di dalam kondisi, perulangan, atau fungsi bersarang. Kedua: panggil hooks hanya dari komponen fungsional React atau hooks kustom. Plugin ESLint eslint-plugin-react-hooks secara otomatis memeriksa kedua aturan.

Kesalahan paling umum — pelanggaran urutan pemanggilan hooks. React bergantung pada urutan pemanggilan hooks antar render. Jika hook berada di dalam kondisi dan tidak dipanggil di salah satu render, semua hook berikutnya bergeser, menyebabkan bug.

KesalahanApa yang terjadiSolusi
Hook dalam kondisiUrutan hooks rusakPindahkan logika kondisi ke dalam hook
Stale closureEfek menggunakan nilai lamaTambahkan semua dependensi ke array
Perulangan tak terbatasuseEffect tanpa dependensi mengubah stateTentukan dependensi atau gunakan useReducer
Mengubah ref di renderEfek samping di badan fungsiPindahkan mutasi ke useEffect

Kesalahan umum kedua — stale closure di useEffect. Jika dalam efek digunakan variabel yang tidak ditentukan dalam dependensi, efek ‘menangkap’ nilainya pada saat pembuatan penutupan. Solusi — selalu sertakan semua variabel yang digunakan dalam array dependensi. Jika terlalu banyak dependensi, bagi efek menjadi beberapa bagian.

Pertanyaan Umum

Mengapa hooks tidak bisa dipanggil dalam kondisi?

React bergantung pada urutan pemanggilan hooks antar render. Jika hook dipanggil dalam kondisi dan tidak bekerja di salah satu render, semua hook berikutnya ‘bergeser’ — state mereka tercampur. Aturan ESLint hooks/exhaustive-deps memperingatkan tentang hal ini.

Apa perbedaan antara useEffect dan useLayoutEffect?

useLayoutEffect sinkron dan dijalankan sebelum browser menggambar perubahan. useEffect asinkron — setelah penggambaran. Gunakan useLayoutEffect hanya ketika perlu mengukur atau mengubah DOM sebelum pengguna melihat perubahan (posisi popover, pemulihan scroll).

Bagaimana membatalkan permintaan fetch saat pelepasan?

Gunakan AbortController di dalam useEffect. Buat controller, berikan signal ke fetch, dan di fungsi cleanup panggil controller.abort(). Untuk browser lama, flag cancelled sudah cukup: return () => { cancelled = true; } dengan pemeriksaan sebelum setState.

Apa itu hook kustom dan kapan membuatnya?

Hook kustom — fungsi JavaScript dengan prefiks use yang menggunakan hooks bawaan. Buatlah ketika logika yang sama dengan hooks berulang di dua atau lebih komponen. Contoh: useDebounce, useLocalStorage, useMediaQuery. Hook kustom menggantikan HOC dan render props.

Mengapa useEffect dijalankan dua kali di Strict Mode?

Di Strict Mode, React sengaja memasang dan melepas komponen dua kali dalam mode pengembangan untuk menemukan kesalahan dengan efek yang tidak dibersihkan. Jika pembersihan ditulis dengan benar, pemanggilan ganda tidak menyebabkan masalah. Dalam mode produksi hal ini tidak terjadi.

Ringkasan

  • React Hooks — fungsi untuk menggunakan state, efek, dan konteks di komponen fungsional, yang menggantikan kelas.
  • useState — hook dasar untuk state lokal dengan setter fungsional untuk pembaruan yang aman.
  • useEffect — hook universal untuk efek samping dengan array dependensi dan fungsi pembersihan.
  • useContext — akses langsung ke konteks React tanpa pembungkus Consumer dan sarang tambahan.
  • useReducer — untuk logika state kompleks dengan tipe aksi, alternatif Redux dalam satu komponen.
  • useRef, useMemo, useCallback — alat optimasi: referensi DOM, memoisasi nilai dan callback.
  • Aturan tingkat atas — batasan ketat pemanggilan hooks di luar kondisi, perulangan, dan fungsi bersarang.

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga