React Hooks: useState dan useEffect untuk Pemula

12 September 2025
•
5 menit

Memahami konsep dasar React Hooks dengan useState dan useEffect melalui contoh praktis dan best practices.

reacthooksuseStateuseEffect

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:

example.tsx
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} 13

Keterangan: tombol akan menambah nilai count setiap kali diklik.

State dengan Object

Ketika menyimpan objek di state, gunakan functional update untuk menjaga immutability:

example.tsx
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} 18

useEffect: Side Effects dan Lifecycle

Hook useEffect menggantikan componentDidMount, componentDidUpdate, dan componentWillUnmount.

Basic Effect

Contoh melakukan fetch data saat component mount:

example.tsx
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} 21

Effect dengan Dependencies

Gunakan dependency array untuk menjalankan effect saat nilai tertentu berubah:

example.tsx
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} 28

Cleanup Function

Contoh effect yang membutuhkan cleanup (mis. interval atau subscription):

example.tsx
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} 15

Best Practices

1. Multiple useState untuk State yang Tidak Related

Gunakan beberapa state terpisah untuk nilai yang tidak terkait:

example.tsx
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 }); 8

2. Custom Hooks untuk Logic Reuse

Pisahkan logic reusable ke dalam custom hooks:

example.tsx
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} 25

3. Optimasi dengan useCallback dan useMemo

Gunakan useMemo dan useCallback untuk menghindari perhitungan ulang yang mahal atau re-render yang tidak perlu:

example.tsx
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} 25

Common Pitfalls

1. Stale Closure

Contoh masalah stale closure dan solusinya:

example.tsx
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} 30

Kesimpulan

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!

Bagikan artikel: