πŸ“š Learning Hub
Β· 3 min read

Code Review This: A React Component with 14 Problems


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: React Interview Questions Β· How React Virtual Dom Works

πŸ“˜