Hereβs a React component written by a junior developer. It works, but it has 14 problems. How many can you spot before scrolling to the answers?
The component
import React, { useState, useEffect } from 'react';
function UserDashboard(props) {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [search, setSearch] = useState('');
useEffect(() => {
fetch('http://localhost:3000/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
});
});
const deleteUser = (id) => {
fetch(`http://localhost:3000/api/users/${id}`, { method: 'DELETE' });
setUsers(users.filter(u => u.id != id));
};
const filteredUsers = users.filter(u =>
u.name.toLowerCase().includes(search)
);
return (
<div>
<h1 style={{color: 'red'}}>User Dashboard</h1>
<input
type="text"
onChange={(e) => setSearch(e.target.value)}
placeholder="Search..."
/>
{loading && <p>Loading...</p>}
<table>
<tr>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
{filteredUsers.map(user => (
<tr>
<td>{user.name}</td>
<td dangerouslySetInnerHTML={{__html: user.email}} />
<td>
<button onClick={deleteUser(user.id)}>Delete</button>
</td>
</tr>
))}
</table>
</div>
);
}
export default UserDashboard;
Take a minute. Count the issues. Then scroll down.
The 14 problems
1. π useEffect runs on every render
// β No dependency array = runs after EVERY render
useEffect(() => { fetch(...) });
// β
Empty array = runs once on mount
useEffect(() => { fetch(...) }, []);
This creates an infinite loop: fetch β setState β re-render β fetch β setStateβ¦
2. π Loading state never set to true
// β loading starts false, gets set to false. Never true.
const [loading, setLoading] = useState(false);
// β
Set loading before fetch
setLoading(true);
fetch(...)
.then(...)
.finally(() => setLoading(false));
3. π Hardcoded localhost URL
// β Won't work in production
fetch('http://localhost:3000/api/users')
// β
Use relative URL or environment variable
fetch('/api/users')
4. β No error handling on fetch
// β If the API fails, nothing happens
fetch(url).then(res => res.json())
// β
Handle errors
fetch(url)
.then(res => {
if (!res.ok) throw new Error('Failed to fetch');
return res.json();
})
.catch(err => setError(err.message));
5. β‘ Delete doesnβt await the API call
// β Updates UI before knowing if delete succeeded
fetch(`/api/users/${id}`, { method: 'DELETE' });
setUsers(users.filter(...));
// β
Wait for confirmation
const res = await fetch(`/api/users/${id}`, { method: 'DELETE' });
if (res.ok) setUsers(users.filter(...));
6. π Loose equality comparison
// β Loose equality: '1' == 1 is true
users.filter(u => u.id != id)
// β
Strict equality
users.filter(u => u.id !== id)
7. π Search doesnβt lowercase the input
// β Searching "John" won't match "john"
u.name.toLowerCase().includes(search)
// β
Lowercase both sides
u.name.toLowerCase().includes(search.toLowerCase())
8. π Missing key prop on mapped elements
// β No key
{filteredUsers.map(user => <tr>...</tr>)}
// β
Add key
{filteredUsers.map(user => <tr key={user.id}>...</tr>)}
9. ποΈ Table missing thead/tbody
// β Invalid HTML structure
<table><tr><th>...</th></tr>{rows}</table>
// β
Proper structure
<table>
<thead><tr><th>...</th></tr></thead>
<tbody>{rows}</tbody>
</table>
10. π XSS vulnerability with dangerouslySetInnerHTML
// β If user.email contains <script>, it executes
<td dangerouslySetInnerHTML={{__html: user.email}} />
// β
Just render the text
<td>{user.email}</td>
This is a critical security vulnerability. Never use dangerouslySetInnerHTML with user data.
11. π±οΈ onClick calls function immediately
// β Calls deleteUser during render, not on click
<button onClick={deleteUser(user.id)}>
// β
Wrap in arrow function
<button onClick={() => deleteUser(user.id)}>
12. βΏ Input missing label
// β No label for screen readers
<input type="text" placeholder="Search..." />
// β
Add a label
<label htmlFor="search" className="sr-only">Search users</label>
<input id="search" type="text" placeholder="Search..." />
13. π¨ Inline styles
// β Inline styles are hard to maintain
<h1 style={{color: 'red'}}>
// β
Use CSS classes
<h1 className="dashboard-title">
14. π¦ Unused import
// β React import not needed in React 17+
import React, { useState, useEffect } from 'react';
// β
Only import what you use
import { useState, useEffect } from 'react';
Scoring
- 12-14: Senior developer eyes π
- 8-11: Solid mid-level reviewer
- 5-7: Getting there β keep practicing
- 0-4: Time to study up on React patterns
The scariest one is #10 (XSS). In a real code review, thatβs the one that should block the PR immediately.
Related resources
Related: React Interview Questions Β· How React Virtual Dom Works