DP
Kembali ke Tutorial
ReactPemula

Memahami React Hooks dari Nol

Pelajari React Hooks melalui alur bertahap: state, effect, context, custom hook, pola fetch data, dan latihan kecil yang dekat dengan project nyata.

8 menit baca28 Mei 2026oleh Reza Maulana

Memahami React Hooks dari Nol

React Hooks membantu kita menulis komponen React berbasis fungsi yang tetap bisa memiliki state, membaca context, menjalankan side effect, dan membungkus logika agar dapat digunakan ulang. Bagi pemula, hooks sering terasa membingungkan karena bentuknya sederhana, tetapi perilakunya sangat terkait dengan cara React melakukan render ulang.

Tutorial ini membahas hooks dengan pendekatan bertahap. Kita tidak hanya melihat sintaks, tetapi juga alasan kapan sebuah hook dipakai, apa konsekuensinya terhadap render, dan kesalahan umum yang perlu dihindari saat membangun aplikasi.

Tujuan Belajar

Setelah mengikuti tutorial ini, kamu diharapkan mampu:

  • Menjelaskan perbedaan data biasa, state, dan props.
  • Menggunakan useState untuk menyimpan data yang memengaruhi tampilan.
  • Menggunakan useEffect untuk menjalankan side effect dengan dependency yang tepat.
  • Menggunakan useContext untuk membaca data global sederhana.
  • Membuat custom hook untuk membungkus logika yang berulang.
  • Membaca gejala bug umum seperti infinite render, stale state, dan dependency yang hilang.

Prasyarat

Kamu tidak harus mahir React, tetapi sebaiknya sudah paham dasar berikut:

PrasyaratMengapa penting
JavaScript functionKomponen React modern biasanya ditulis sebagai function.
Array dan objectState sering berbentuk array atau object.
CallbackEvent handler seperti onClick menerima function.
Promise dasarBanyak side effect berkaitan dengan fetch data.

Jika beberapa bagian belum kuat, tetap lanjutkan. Fokus utama tutorial ini adalah pola berpikir hooks, bukan menghafal seluruh API React.

Mental Model: Render Adalah Snapshot

Ketika React menjalankan komponen, React menghasilkan sebuah snapshot UI berdasarkan props dan state pada saat itu. Jika state berubah, React menjalankan ulang komponen untuk menghasilkan snapshot baru.

Perhatikan contoh sederhana berikut.

jsx
function Greeting({ name }) {
  const message = `Halo, ${name}`;
  return <p>{message}</p>;
}

message bukan state. Nilainya dihitung setiap render. Jika name berubah, komponen dirender ulang dan message dihitung ulang. State baru dibutuhkan ketika sebuah nilai perlu bertahan antar-render dan perubahannya harus membuat UI diperbarui.

useState: Menyimpan Nilai yang Mengubah UI

useState adalah hook paling dasar. Ia mengembalikan dua hal: nilai saat ini dan function untuk meminta React memperbarui nilai tersebut.

jsx
import { useState } from "react";

export function Counter() {
  const [count, setCount] = useState(0);

  return (
    <section>
      <p>Jumlah klik: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Tambah
      </button>
    </section>
  );
}

Ada tiga hal penting dari contoh ini. Pertama, count adalah nilai untuk render saat ini. Kedua, setCount tidak mengubah variabel count secara langsung. Ketiga, setelah setCount dipanggil, React menjadwalkan render baru dengan nilai terbaru.

State Berdasarkan Nilai Sebelumnya

Jika nilai baru bergantung pada nilai lama, gunakan bentuk callback. Ini menghindari bug saat beberapa update terjadi berdekatan.

jsx
function CounterActions() {
  const [count, setCount] = useState(0);

  function incrementThreeTimes() {
    setCount((current) => current + 1);
    setCount((current) => current + 1);
    setCount((current) => current + 1);
  }

  return (
    <button onClick={incrementThreeTimes}>
      Tambah 3 dari {count}
    </button>
  );
}

Callback menerima nilai state paling mutakhir yang diketahui React. Pola ini cocok untuk counter, toggle, antrean, keranjang belanja, dan data yang diperbarui beberapa kali dari interaksi yang sama.

State Object dan Array Harus Immutable

React mendeteksi perubahan berdasarkan referensi. Karena itu, jangan mengubah object atau array lama secara langsung. Buat salinan baru.

jsx
function ProfileForm() {
  const [profile, setProfile] = useState({
    name: "",
    role: "student",
  });

  function updateName(event) {
    setProfile((current) => ({
      ...current,
      name: event.target.value,
    }));
  }

  return (
    <label>
      Nama
      <input value={profile.name} onChange={updateName} />
    </label>
  );
}

Untuk array, gunakan map, filter, atau spread, bukan push langsung pada state lama.

jsx
function TodoList() {
  const [todos, setTodos] = useState([]);

  function addTodo(title) {
    setTodos((current) => [
      ...current,
      { id: crypto.randomUUID(), title, done: false },
    ]);
  }

  function toggleTodo(id) {
    setTodos((current) =>
      current.map((todo) =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      )
    );
  }

  return null;
}

useEffect: Menjalankan Side Effect

Render sebaiknya tetap murni: menerima input, menghasilkan UI. Aktivitas seperti fetch data, membaca storage, memasang event listener, atau mengubah title dokumen termasuk side effect. Untuk kebutuhan seperti itu, gunakan useEffect.

jsx
import { useEffect, useState } from "react";

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [status, setStatus] = useState("idle");

  useEffect(() => {
    let cancelled = false;

    async function loadUser() {
      setStatus("loading");
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();

      if (!cancelled) {
        setUser(data);
        setStatus("success");
      }
    }

    loadUser().catch(() => {
      if (!cancelled) setStatus("error");
    });

    return () => {
      cancelled = true;
    };
  }, [userId]);

  if (status === "loading") return <p>Memuat data...</p>;
  if (status === "error") return <p>Gagal memuat data.</p>;
  return <pre>{JSON.stringify(user, null, 2)}</pre>;
}

Dependency array [userId] berarti effect berjalan saat komponen pertama muncul dan setiap kali userId berubah. Cleanup function digunakan agar hasil request lama tidak menimpa state ketika komponen sudah berganti konteks.

Memahami Dependency Array

Dependency array sering menjadi sumber bug. Aturan praktisnya: semua nilai dari luar effect yang dipakai di dalam effect perlu masuk dependency.

BentukKapan berjalanCatatan
Tanpa arraySetelah setiap renderJarang dibutuhkan karena mudah membuat loop.
[]Setelah mountCocok untuk setup yang tidak bergantung props/state.
[value]Setelah mount dan saat value berubahIni pola paling umum.

Contoh bug umum:

jsx
function BadSearch({ query }) {
  const [result, setResult] = useState(null);

  useEffect(() => {
    fetch(`/api/search?q=${query}`)
      .then((response) => response.json())
      .then(setResult);
  }, []);

  return <pre>{JSON.stringify(result)}</pre>;
}

Kode di atas hanya mencari berdasarkan query awal. Jika query berubah, effect tidak berjalan lagi. Dependency yang benar adalah [query].

useContext: Membaca Nilai Lintas Komponen

Context berguna ketika beberapa komponen perlu membaca data yang sama tanpa mengoper props terlalu jauh. Contoh ringan adalah theme, user session, atau preferensi bahasa.

jsx
import { createContext, useContext } from "react";

const ThemeContext = createContext("light");

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

  return (
    <button className={theme === "dark" ? "button-dark" : "button-light"}>
      Simpan
    </button>
  );
}

export function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}

Context bukan pengganti semua state management. Jika setiap perubahan context membuat banyak komponen render ulang, pertimbangkan memecah context atau memakai state management yang lebih sesuai.

Custom Hook: Membungkus Logika Berulang

Custom hook adalah function yang namanya diawali use dan boleh memanggil hooks lain. Tujuannya bukan membuat kode terlihat keren, melainkan memindahkan pola yang berulang ke tempat yang lebih mudah diuji dan dibaca.

jsx
import { useEffect, useState } from "react";

function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    if (typeof window === "undefined") return initialValue;
    const stored = window.localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  useEffect(() => {
    window.localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
}

function NameForm() {
  const [name, setName] = useLocalStorage("display-name", "");

  return (
    <input
      value={name}
      onChange={(event) => setName(event.target.value)}
      placeholder="Nama tampilan"
    />
  );
}

Custom hook membuat komponen fokus pada UI, sedangkan detail sinkronisasi dengan storage berada di satu tempat.

Rules of Hooks

Ada dua aturan inti:

  • Panggil hooks hanya di level teratas komponen atau custom hook.
  • Panggil hooks hanya dari React component atau custom hook.

Jangan menaruh hooks di dalam kondisi, loop, atau function biasa.

jsx
function BadComponent({ enabled }) {
  if (enabled) {
    const [value, setValue] = useState("");
  }

  return null;
}

React mengandalkan urutan pemanggilan hooks yang sama pada setiap render. Jika hooks dipanggil secara kondisional, urutan itu berubah dan React tidak bisa memasangkan state dengan benar.

Kesalahan Umum

KesalahanDampakPerbaikan
Mutasi state langsungUI tidak selalu updateBuat object atau array baru.
Dependency effect kosong padahal memakai propsData tidak sinkronMasukkan dependency yang dipakai.
Menaruh fetch langsung di renderRequest berulang tiap renderPindahkan ke effect atau framework data layer.
Terlalu cepat membuat custom hookAbstraksi sulit dipahamiEkstrak setelah pola berulang terlihat.

Coba Sendiri

Buat komponen ReadingTracker dengan fitur berikut:

  • Menampilkan daftar materi yang sedang dibaca.
  • Tombol untuk menandai materi selesai.
  • Progress berupa jumlah materi selesai dari total materi.
  • Simpan progress ke localStorage menggunakan custom hook.

Contoh data awal:

jsx
const lessons = [
  { id: "state", title: "State dan Props" },
  { id: "effect", title: "Side Effect" },
  { id: "context", title: "Context" },
];

Mulai dari useState, lalu baru pindahkan logika storage ke custom hook setelah komponen berjalan.

Cek Pemahaman

Kuis

Kapan nilai sebaiknya disimpan sebagai state?

Pilih satu jawaban.

Kuis

Apa tujuan dependency array pada useEffect?

Pilih satu jawaban.

Ringkasan

React Hooks bukan sekadar API baru untuk mengganti class component. Hooks adalah cara menyusun logika UI berdasarkan render, state, dan side effect. Mulailah dari mental model bahwa render adalah snapshot. Gunakan useState untuk data yang memengaruhi UI, useEffect untuk side effect, useContext untuk data lintas komponen yang sederhana, dan custom hook saat ada pola logika yang benar-benar berulang.

#react#hooks#javascript#frontend#state
Apakah konten ini membantu?

Feedback kamu membantu kami memperbaiki materi berikutnya.

Diskusi (0)

Silakan masuk terlebih dahulu untuk ikut berdiskusi

Login Sekarang