Debugging React Applications
React memiliki tantangan debugging unik karena virtual DOM, state management, dan component lifecycle. Berikut tools dan teknik khusus untuk React.
React DevTools Extension
Install React Developer Tools dari Chrome Web Store. Ini menambah 2 tab baru di DevTools:
Components Tab
Menampilkan component tree (bukan DOM tree):
App
├── Header
│ ├── Logo
│ └── Navigation
│ ├── NavLink (to="/")
│ └── NavLink (to="/about")
├── MainContent
│ └── UserProfile
│ ├── Avatar
│ └── UserInfo
└── Footer
Untuk setiap component, kamu bisa melihat:
- Props — data yang diterima dari parent
- State — state internal component
- Hooks — nilai dari useState, useEffect, dll
- Source — lokasi file component
Kamu juga bisa mengedit props dan state secara live — sangat berguna untuk testing berbagai state.
Profiler Tab
Untuk mengukur performance rendering. Klik record → lakukan aksi → stop recording. Kamu bisa melihat:
- Component mana yang re-render
- Berapa lama setiap render
- Apa yang memicu re-render
Bug Umum di React
1. Infinite Re-render
// ❌ useEffect tanpa dependency → infinite loop
useEffect(() => {
setCount(count + 1); // set state → trigger re-render → trigger effect → ...
});
// ✅ Tambahkan dependency array
useEffect(() => {
fetchData();
}, []); // [] = hanya sekali saat mount
// ❌ Object/array baru setiap render sebagai dependency
useEffect(() => {
// ini trigger setiap render karena {} !== {}
}, [{ filter: "active" }]);
// ✅ Gunakan primitive value atau useMemo
useEffect(() => {
// ini hanya trigger saat filter berubah
}, [filter]);
2. Stale State/Closure
// ❌ State stale di dalam callback
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setTimeout(() => {
console.log(count); // Selalu nilai saat handleClick dipanggil!
setCount(count + 1); // Bisa salah jika diklik cepat
}, 1000);
};
// ✅ Gunakan functional update
const handleClickFixed = () => {
setTimeout(() => {
setCount(prev => prev + 1); // Selalu berdasarkan state terbaru
}, 1000);
};
}
3. Missing Key di List
// ❌ Warning: Each child should have a unique "key" prop
items.map(item => <ListItem data={item} />)
// ✅ Tambahkan key unik
items.map(item => <ListItem key={item.id} data={item} />)
// ❌ Jangan gunakan index sebagai key jika list bisa berubah
items.map((item, index) => <ListItem key={index} data={item} />)
4. Props Undefined
// ❌ TypeError: Cannot read properties of undefined
function UserProfile({ user }) {
return <h1>{user.name}</h1>; // Jika user belum di-fetch
}
// ✅ Defensive rendering
function UserProfile({ user }) {
if (!user) return <p>Loading...</p>;
return <h1>{user.name}</h1>;
}
// ✅ Optional chaining
function UserProfile({ user }) {
return <h1>{user?.name ?? "Unknown"}</h1>;
}
Teknik Debugging React
Console.log di Render
function MyComponent({ data }) {
console.log("MyComponent render:", { data });
// atau gunakan useEffect untuk track re-renders
useEffect(() => {
console.log("MyComponent mounted/updated");
});
return <div>{data.title}</div>;
}
Why Did You Render?
Library @welldone-software/why-did-you-render membantu mendeteksi re-render yang tidak perlu.
Error Boundary
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error("Error caught:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return <h1>Terjadi kesalahan: {this.state.error.message}</h1>;
}
return this.props.children;
}
}
// Bungkus component yang rawan error
<ErrorBoundary>
<UserProfile />
</ErrorBoundary>
Tips
- Selalu install React DevTools — inspect state dan props jauh lebih mudah
- Gunakan StrictMode di development — membantu mendeteksi masalah umum
- Perhatikan console warnings React — mereka biasanya sangat informatif
- Gunakan Profiler jika aplikasi terasa lambat
- Biasakan defensive rendering — selalu handle null/undefined