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
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 — 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.
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>
);
}
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 — 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.
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 — 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.
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 — 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.
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 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.
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).
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.
| Kesalahan | Apa yang terjadi | Solusi |
|---|---|---|
| Hook dalam kondisi | Urutan hooks rusak | Pindahkan logika kondisi ke dalam hook |
| Stale closure | Efek menggunakan nilai lama | Tambahkan semua dependensi ke array |
| Perulangan tak terbatas | useEffect tanpa dependensi mengubah state | Tentukan dependensi atau gunakan useReducer |
| Mengubah ref di render | Efek samping di badan fungsi | Pindahkan 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
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.
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).
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.
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.
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
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.
Baca juga