DEV Community

Cover image for 10 Common React Mistakes New Developers Make (and How to Fix Them)
Hari Lamichhane
Hari Lamichhane Subscriber

Posted on

10 Common React Mistakes New Developers Make (and How to Fix Them)

Hey React developers,

After working with dozens of developers and reviewing lots of React code, I’ve noticed a pattern. Many developers write code that works fine during development but leads to subtle bugs and frustrating user experiences in production.

These issues often show up when real users interact with the app, leading to bounce rates, lost user trust, or even lost revenue.

In this post, I’ll break down 10 common mistakes I see (and have made myself), plus practical ways to fix them. The goal: move from "it works on my machine" to "this feels great for everyone."

1. Not Using URL Query Parameters

The Mistake:

const [search, setSearch] = useState('');
Enter fullscreen mode Exit fullscreen mode

State is stored in memory only.

Why It’s a Problem:

  • Refreshing the page resets filters.
  • You can’t share a filtered view.
  • The URL doesn’t reflect the UI state.

How to Fix It:

Use useSearchParams for simple cases:

const [searchParams, setSearchParams] = useSearchParams();
const search = searchParams.get('q') || '';
Enter fullscreen mode Exit fullscreen mode

Use nuqs + zod for validated, typed state:

import { useQueryState } from 'nuqs';
import { z } from 'zod';

const { q, setQuery } = useQueryState({ q: z.string().optional() });

const handleSearch = (value) => {
  setQuery({ q: value });
};
Enter fullscreen mode Exit fullscreen mode

Browsers treat URLs as the source of truth, so should your UI.


2. Avoiding the <form> Element

The Mistake:

Using divs and onClick handlers instead of native <form> behavior.

Why It’s a Problem:

  • Enter key doesn’t work.
  • Screen readers miss semantic context.
  • Browser validations are skipped.

How to Fix It:

<form onSubmit={(e) => {
  e.preventDefault();
  handleSubmit();
}}>
  <label htmlFor="name">Name:</label>
  <input id="name" type="text" required />
  <button type="submit">Submit</button>
</form>
Enter fullscreen mode Exit fullscreen mode

Libraries like react-hook-form still rely on <form>.


3. Too Many Independent useState Calls

The Mistake:

const [name, setName] = useState('');
const [email, setEmail] = useState('');
Enter fullscreen mode Exit fullscreen mode

Why It’s a Problem:

It becomes hard to manage and update as the form grows.

How to Fix It:

Use an object:

const [form, setForm] = useState({ name: '', email: '' });
const updateField = (field, value) => {
  setForm(prev => ({ ...prev, [field]: value }));
};
Enter fullscreen mode Exit fullscreen mode

Or use useReducer for complex cases:

const initialState = { name: '', email: '' };
function reducer(state, action) {
  switch (action.type) {
    case 'UPDATE_FIELD':
      return { ...state, [action.field]: action.value };
    default:
      return state;
  }
}
const [form, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

4. Not Using Derived State

The Mistake:

const [birthDate, setBirthDate] = useState('');
const [age, setAge] = useState(calculateAge(birthDate));
Enter fullscreen mode Exit fullscreen mode

Why It’s a Problem:

Duplicated state leads to stale or incorrect values.

How to Fix It:

const age = calculateAge(birthDate);
Enter fullscreen mode Exit fullscreen mode

Use useMemo only for expensive calculations:

const sortedItems = useMemo(() => {
  return items.sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
Enter fullscreen mode Exit fullscreen mode

5. Misusing useMemo

The Mistake:

const memoized = useMemo(() => doSomething(input), [valueThatChangesOnEveryRender]);
Enter fullscreen mode Exit fullscreen mode

Why It’s a Problem:

It runs on every render; this is pointless.

How to Fix It:

Only use useMemo when:

  • The computation is expensive.
  • Dependencies are stable.
const expensiveResult = useMemo(() => computeExpensiveValue(data), [data]);
Enter fullscreen mode Exit fullscreen mode

6. No Loading, Error, or Empty States

The Mistake:

const { data } = useQuery(...);
return <List items={data} />;
Enter fullscreen mode Exit fullscreen mode

Why It’s a Problem:

  • No feedback while loading.
  • Users see blank screens on failure.
  • SEO crawlers might index empty pages.

How to Fix It:

const { data, isLoading, isError, error } = useQuery(...);
if (isLoading) return <SkeletonList />;
if (isError) return <p>Error: {error.message}</p>;
if (!data?.length) return <p>No items found.</p>;
return <List items={data} />;
Enter fullscreen mode Exit fullscreen mode

Use @tanstack/react-query to simplify handling these states.


7. Ignoring Accessibility

The Mistake:

Using <div> instead of <button>, skipping label, or forgetting alt text.

Why It’s a Problem:

  • Breaks keyboard navigation.
  • Screen readers can’t interpret the UI.

How to Fix It:

<label htmlFor="username">Username:</label>
<input id="username" type="text" />

<button aria-expanded={isOpen} onClick={toggle}>Toggle</button>
Enter fullscreen mode Exit fullscreen mode

Add alt text to images. Test with your keyboard. Use eslint-plugin-jsx-a11y or axe.


8. Input Without Debounce

The Mistake:

<input value={query} onChange={e => setQuery(e.target.value)} />
Enter fullscreen mode Exit fullscreen mode

Why It’s a Problem:

Triggers expensive updates on every keystroke.

How to Fix It:

Use a debounce hook:

function useDebounce(value, delay) {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const id = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(id);
  }, [value, delay]);
  return debounced;
}
Enter fullscreen mode Exit fullscreen mode

Or use useDeferredValue (React 18+) for smoother UI transitions.


9. Multi-Step Forms That Reset on Navigation

The Mistake:

Each step owns its own state.

Why It’s a Problem:

Going back resets user input.

How to Fix It:

Store state at the parent level:

const [formState, setFormState] = useState({ name: '', email: '' });
Enter fullscreen mode Exit fullscreen mode

Pass props into each step. For large apps, use react-hook-form or zustand to manage form state.


10. No Skeletons or Placeholders

The Mistake:

return isLoading ? null : <ActualList items={data} />;
Enter fullscreen mode Exit fullscreen mode

Why It’s a Problem:

Blank screen. Confusing for users. Bad perceived performance.

How to Fix It:

function SkeletonList() {
  return (
    <div>
      {[...Array(3)].map((_, i) => (
        <div key={i} style={{ height: '50px', background: '#e0e0e0', marginBottom: '8px' }} />
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Skeletons provide users with feedback and reduce layout shifts.


Tools That Solve Most of These

Start with React’s built-in hooks for small projects. For larger apps, reach for:

  • @tanstack/react-query — handles fetching, caching, and background syncing.
  • nuqs + zod — URL query param parsing and validation.
  • zod / yup — validation schemas for user input.
  • react-hook-form — performant, scalable form handling.
  • zustand / valtio / jotai — global state without boilerplate.
  • eslint-plugin-react-hooks / jsx-a11y — catch common bugs early.

Final Thoughts

Poor UX often begins with minor oversights in code. These mistakes might not show up in local dev, but they frustrate real users. Write code that survives refreshes, plays nice with URLs, and shows empathy for edge cases.

None of this is about being perfect. It’s about being thoughtful.

I’ve also made these mistakes. That’s how I learned.

What about you? Which of these have you made? Share your story or question, I’d love to hear from you.

Top comments (7)

Collapse
 
nevodavid profile image
Nevo David

Man, I’ve done half of these myself - love seeing this kinda breakdown, makes it way easier to spot my own mess ups.

Collapse
 
harimanok profile image
Hari Lamichhane

Yes, me too!

Collapse
 
danielheim profile image
Daniel Heim

Great article.
Points 6 could be improved with the new useActionState hook, as it can manage loading state and form submission.

Collapse
 
harimanok profile image
Hari Lamichhane

Yup, for server actions.

Collapse
 
srinivasant profile image
srinivasan

Helpful and spot-on! Every React beginner should save this, understanding these mistakes early can save hours of debugging and frustration.

Collapse
 
harimanok profile image
Hari Lamichhane

Thanks!

Collapse
 
nathan_tarbert profile image
Nathan Tarbert

been cool seeing steady progress - it adds up. you think habits or just the grind keeps you pushing forward with stuff like this?

Some comments may only be visible to logged-in visitors. Sign in to view all comments.