ReactJS #23: Membuat Custom Hook
Meminjam Kode untuk Dipakai Ulang di Banyak Tempat
Halo, penemu ulang! 🔧
Pernahkah kamu berada di perpustakaan dan menemukan buku yang sangat berguna? Kamu bisa meminjamnya dan membacanya berkali-kali. Nah, di React, kita juga bisa membuat kode yang bisa dipinjam dan dipakai ulang di banyak komponen. Kode itu disebut Custom Hook!
💡 Analogi: Bayangkan kamu punya alat ajaib (custom hook) yang bisa kamu bawa ke mana saja. Alat ini bisa melakukan tugas tertentu, misalnya mengambil data dari internet, mengelola input form, atau mendeteksi ukuran layar. Kamu tinggal pakai alat itu di komponen mana pun tanpa harus menulis ulang kodenya. Praktis, kan?
🧐 Apa Itu Custom Hook?
Custom Hook adalah fungsi JavaScript yang namanya diawali dengan use (misal useFetch, useInput) dan bisa memanggil hook React lainnya (seperti useState, useEffect, dll). Dengan custom hook, kita bisa memisahkan logika yang kompleks dari komponen, sehingga komponen menjadi lebih bersih dan logika bisa dipakai ulang.
Aturan penting:
- Nama harus diawali
use (agar React tahu ini adalah hook).
- Hanya dipanggil di tingkat atas komponen atau custom hook lain (sama seperti hook biasa).
- Bisa menerima argumen dan mengembalikan nilai apa pun (array, objek, dll).
📦 Contoh 1: Custom Hook untuk Fetch Data (useFetch)
Kita sering mengambil data dari API dengan useEffect dan useState. Mari kita buat custom hook useFetch yang bisa dipakai ulang.
Buat file hooks/useFetch.js:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url);
if (!response.ok) throw new Error('Gagal mengambil data');
const result = await response.json();
setData(result);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]); // efek dijalankan ulang jika url berubah
return { data, loading, error };
}
export default useFetch;
Kemudian gunakan di komponen:
import useFetch from './hooks/useFetch';
function PostList() {
const { data: posts, loading, error } = useFetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
if (loading) return <p>⏳ Memuat...</p>;
if (error) return <p>❌ {error}</p>;
return (
<div>
<h3>📰 Daftar Postingan</h3>
<ul>
{posts.map(post => <li key={post.id}>{post.title}</li>)}
</ul>
</div>
);
}
Lihat! Komponen jadi sangat bersih. Semua logika fetch tersembunyi di custom hook.
📝 Contoh 2: Custom Hook untuk Input Form (useInput)
Menangani input form biasanya melibatkan state dan onChange. Kita bisa buat hook useInput.
import { useState } from 'react';
function useInput(initialValue = '') {
const [value, setValue] = useState(initialValue);
const onChange = (e) => {
setValue(e.target.value);
};
const reset = () => setValue(initialValue);
return {
value,
onChange,
reset,
bind: { value, onChange } // untuk disebar ke input
};
}
export default useInput;
Penggunaan di komponen:
import useInput from './hooks/useInput';
function RegisterForm() {
const nameInput = useInput('');
const emailInput = useInput('');
const handleSubmit = (e) => {
e.preventDefault();
console.log('Nama:', nameInput.value);
console.log('Email:', emailInput.value);
nameInput.reset();
emailInput.reset();
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>Nama:</label>
<input type="text" {...nameInput.bind} />
</div>
<div>
<label>Email:</label>
<input type="email" {...emailInput.bind} />
</div>
<button type="submit">Daftar</button>
</form>
);
}
Teknik ...bind menyebarkan properti value dan onChange ke input. Sangat praktis!
📏 Contoh 3: Custom Hook untuk Ukuran Layar (useWindowSize)
Hook ini berguna untuk membuat desain responsif.
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight
});
useEffect(() => {
const handleResize = () => {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight
});
};
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []); // sekali saat mount
return windowSize;
}
export default useWindowSize;
Penggunaan:
import useWindowSize from './hooks/useWindowSize';
function ResponsiveComponent() {
const { width, height } = useWindowSize();
return (
<div>
<p>Lebar layar: {width}px</p>
<p>Tinggi layar: {height}px</p>
{width < 600 && <p>📱 Mode HP</p>}
</div>
);
}
📐 Demo Ukuran Layar (simulasi)
Lebar layar: 1024px
Tinggi layar: 768px
💻 Mode Desktop
🧩 Manfaat Custom Hook
- Reusable: Sekali buat, bisa dipakai di banyak komponen.
- Terpisah: Logika kompleks dipisahkan dari tampilan, komponen jadi lebih bersih.
- Mudah diuji: Custom hook bisa diuji terpisah.
- Berbagi logika antar komponen: Tanpa harus mengubah struktur komponen (seperti HOC atau render props).
🧪 Latihan: Custom Hook untuk localStorage (useLocalStorage)
Buat custom hook useLocalStorage yang berfungsi seperti useState, tapi tersimpan di localStorage. Gunakan untuk menyimpan preferensi tema atau data form.
Petunjuk:
- Terima
key dan initialValue.
- Baca nilai dari localStorage saat pertama kali (gunakan lazy initializer untuk menghindari baca ulang).
- Gunakan
useEffect untuk menyimpan ke localStorage setiap nilai berubah.
- Kembalikan
[storedValue, setStoredValue] seperti useState.
Kerangka:
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// Baca dari localStorage
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
});
// Simpan ke localStorage setiap storedValue berubah
useEffect(() => {
window.localStorage.setItem(key, JSON.stringify(storedValue));
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
Coba gunakan untuk menyimpan nama pengguna:
function NameForm() {
const [name, setName] = useLocalStorage('name', '');
return (
<div>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Masukkan nama"
/>
<p>Nama tersimpan: {name}</p>
</div>
);
}
Refresh halaman, nama tetap ada!
💡 Tips: Custom hook bisa menggunakan hook lain di dalamnya. Jangan ragu untuk membuat hook kecil yang spesifik, lalu menggabungkannya menjadi hook yang lebih besar.
⚠️ Hal Penting
- Jangan panggil custom hook di dalam kondisi atau loop – sama seperti hook biasa, panggil di tingkat atas komponen.
- Nama harus
useXxx agar React bisa memeriksa aturan hook.
- Custom hook bisa menerima argumen dan mengembalikan apa saja (objek, array, fungsi).
🚀 Kesimpulan
- Custom hook adalah fungsi yang memakai hook React untuk membungkus logika yang bisa dipakai ulang.
- Kita sudah membuat
useFetch, useInput, useWindowSize, dan useLocalStorage.
- Custom hook membuat komponen lebih bersih dan logika lebih terorganisir.
- Dengan custom hook, kita bisa berbagi logika antar komponen tanpa mengubah struktur komponen.
Di artikel selanjutnya kita akan belajar tentang React Developer Tools – alat untuk menginspeksi aplikasi React di browser. Sampai jumpa!