React Hooks: useState dan useEffect untuk Pemula
React Hooks mengubah cara kita menulis komponen React. Dua hooks paling penting untuk dipahami adalah useState dan useEffect.
useState: Managing Component State
Hook useState memungkinkan kita menambahkan state ke functional components.
Syntax Dasar
Contoh sederhana penggunaan useState:
1import { useState } from 'react';
2
3function Counter() {
4 const [count, setCount] = useState(0);
5
6 return (
7 <div>
8 <p>Count: {count}</p>
9 <button onClick={() => setCount(count + 1)}>Increment</button>
10 </div>
11 );
12}
13Keterangan: tombol akan menambah nilai count setiap kali diklik.
State dengan Object
Ketika menyimpan objek di state, gunakan functional update untuk menjaga immutability:
1function UserProfile() {
2 const [user, setUser] = useState({ name: '', email: '', age: 0 });
3
4 const updateName = (newName: string) => {
5 setUser((prev) => ({ ...prev, name: newName }));
6 };
7
8 return (
9 <form>
10 <input
11 value={user.name}
12 onChange={(e) => updateName(e.target.value)}
13 placeholder="Name"
14 />
15 </form>
16 );
17}
18useEffect: Side Effects dan Lifecycle
Hook useEffect menggantikan componentDidMount, componentDidUpdate, dan componentWillUnmount.
Basic Effect
Contoh melakukan fetch data saat component mount:
1import { useState, useEffect } from 'react';
2
3function UserList() {
4 const [users, setUsers] = useState([]);
5
6 useEffect(() => {
7 // Fetch data ketika component mount
8 fetch('/api/users')
9 .then((res) => res.json())
10 .then((data) => setUsers(data));
11 }, []); // Empty dependency array = run once
12
13 return (
14 <ul>
15 {users.map((user) => (
16 <li key={user.id}>{user.name}</li>
17 ))}
18 </ul>
19 );
20}
21Effect dengan Dependencies
Gunakan dependency array untuk menjalankan effect saat nilai tertentu berubah:
1function SearchResults({ query }: { query: string }) {
2 const [results, setResults] = useState([]);
3 const [loading, setLoading] = useState(false);
4
5 useEffect(() => {
6 if (!query) return;
7
8 setLoading(true);
9
10 fetch(`/api/search?q=${query}`)
11 .then((res) => res.json())
12 .then((data) => {
13 setResults(data);
14 setLoading(false);
15 });
16 }, [query]); // Re-run ketika query berubah
17
18 if (loading) return <div>Loading...</div>;
19
20 return (
21 <div>
22 {results.map((item) => (
23 <div key={item.id}>{item.title}</div>
24 ))}
25 </div>
26 );
27}
28Cleanup Function
Contoh effect yang membutuhkan cleanup (mis. interval atau subscription):
1function Timer() {
2 const [seconds, setSeconds] = useState(0);
3
4 useEffect(() => {
5 const interval = setInterval(() => {
6 setSeconds((prev) => prev + 1);
7 }, 1000);
8
9 // Cleanup function
10 return () => clearInterval(interval);
11 }, []);
12
13 return <div>Timer: {seconds}s</div>;
14}
15Best Practices
1. Multiple useState untuk State yang Tidak Related
Gunakan beberapa state terpisah untuk nilai yang tidak terkait:
1// ✅ Good
2const [name, setName] = useState('');
3const [email, setEmail] = useState('');
4const [loading, setLoading] = useState(false);
5
6// ❌ Avoid
7const [state, setState] = useState({ name: '', email: '', loading: false });
82. Custom Hooks untuk Logic Reuse
Pisahkan logic reusable ke dalam custom hooks:
1function useLocalStorage(key: string, initialValue: any) {
2 const [value, setValue] = useState(() => {
3 const item = localStorage.getItem(key);
4 return item ? JSON.parse(item) : initialValue;
5 });
6
7 const setStoredValue = (newValue: any) => {
8 setValue(newValue);
9 localStorage.setItem(key, JSON.stringify(newValue));
10 };
11
12 return [value, setStoredValue];
13}
14
15// Usage
16function Settings() {
17 const [theme, setTheme] = useLocalStorage('theme', 'light');
18
19 return (
20 <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
21 Current theme: {theme}
22 </button>
23 );
24}
253. Optimasi dengan useCallback dan useMemo
Gunakan useMemo dan useCallback untuk menghindari perhitungan ulang yang mahal atau re-render yang tidak perlu:
1import { useState, useEffect, useCallback, useMemo } from 'react';
2
3function ExpensiveComponent({ users }: { users: User[] }) {
4 const [filter, setFilter] = useState('');
5
6 // Memoize expensive calculation
7 const filteredUsers = useMemo(() => {
8 return users.filter((user) =>
9 user.name.toLowerCase().includes(filter.toLowerCase())
10 );
11 }, [users, filter]);
12
13 // Memoize callback function
14 const handleSearch = useCallback((searchTerm: string) => {
15 setFilter(searchTerm);
16 }, []);
17
18 return (
19 <div>
20 <SearchInput onSearch={handleSearch} />
21 <UserList users={filteredUsers} />
22 </div>
23 );
24}
25Common Pitfalls
1. Stale Closure
Contoh masalah stale closure dan solusinya:
1// ❌ Problem: count akan selalu 0
2function BadCounter() {
3 const [count, setCount] = useState(0);
4
5 useEffect(() => {
6 const timer = setInterval(() => {
7 setCount(count + 1); // Stale closure!
8 }, 1000);
9
10 return () => clearInterval(timer);
11 }, []);
12
13 return <div>{count}</div>;
14}
15
16// ✅ Solution: gunakan functional update
17function GoodCounter() {
18 const [count, setCount] = useState(0);
19
20 useEffect(() => {
21 const timer = setInterval(() => {
22 setCount((prev) => prev + 1); // Always current value
23 }, 1000);
24
25 return () => clearInterval(timer);
26 }, []);
27
28 return <div>{count}</div>;
29}
30Kesimpulan
useState dan useEffect adalah foundation React Hooks yang powerful untuk:
- Managing component state
- Handling side effects
- Lifecycle management
Dengan memahami patterns dan best practices di atas, kamu bisa menulis React components yang lebih clean dan maintainable.
Ingin belajar hooks lanjutan seperti useContext, useReducer, dan custom hooks? Check out tutorial selanjutnya!