DEV Community

Sahil Sahu
Sahil Sahu

Posted on

I Learned React Wrong for 3 Months. Here's What I Wish I Knew on Day 1

I spent 3 months learning React.

Built 4 projects. Felt confident. Then got destroyed in my first code review.

"Why are you doing it like this?"

"This isn't how React works."

"Did you learn this from a 2018 tutorial?"

Turns out, I learned React completely wrong.

Here's what I wish someone told me on Day 1.


Mistake #1: I Learned Class Components First

What I did:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return <div>{this.state.count}</div>;
  }
}
Enter fullscreen mode Exit fullscreen mode

Why it's wrong: Class components are legacy. The industry moved to functional components in 2019.

What I should've learned:

function MyComponent() {
  const [count, setCount] = useState(0);
  return <div>{count}</div>;
}
Enter fullscreen mode Exit fullscreen mode

The lesson: Don't learn outdated patterns. Check when the tutorial was made.


Mistake #2: I Used useEffect for Everything

What I did:

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);

  return <div>{user?.name}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Why it's problematic:

  • Race conditions
  • No loading state
  • No error handling
  • Runs on every render if dependencies are wrong

What I should've learned:

function UserProfile({ userId }) {
  const { data: user, isLoading, error } = useQuery(
    ['user', userId],
    () => fetchUser(userId)
  );

  if (isLoading) return <Spinner />;
  if (error) return <Error message={error.message} />;

  return <div>{user.name}</div>;
}
Enter fullscreen mode Exit fullscreen mode

The lesson: Use proper data fetching libraries (React Query, SWR). Don't reinvent the wheel.


Mistake #3: I Put Everything in useState

What I did:

function Form() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [age, setAge] = useState('');
  const [address, setAddress] = useState('');
  // ... 10 more useState calls
}
Enter fullscreen mode Exit fullscreen mode

Why it's messy: Too many state variables. Hard to manage. Causes unnecessary re-renders.

What I should've learned:

function Form() {
  const [formData, setFormData] = useReducer(
    (state, action) => ({ ...state, ...action }),
    { name: '', email: '', age: '', address: '' }
  );

  const handleChange = (e) => {
    setFormData({ [e.target.name]: e.target.value });
  };
}
Enter fullscreen mode Exit fullscreen mode

The lesson: Related state should be grouped. Use useReducer for complex state.


Mistake #4: I Prop-Drilled Like Crazy

What I did:

<App>
  <Header user={user} theme={theme} />
    <Sidebar user={user} theme={theme} />
      <Content user={user} theme={theme} />
        <Footer user={user} theme={theme} />
Enter fullscreen mode Exit fullscreen mode

Why it sucks: Passing props through 5 levels. Nightmare to maintain.

What I should've learned:

// Context for global state
const UserContext = createContext();

function App() {
  return (
    <UserContext.Provider value={user}>
      <Header />
      <Sidebar />
      <Content />
    </UserContext.Provider>
  );
}

function Header() {
  const user = useContext(UserContext);
  return <div>{user.name}</div>;
}
Enter fullscreen mode Exit fullscreen mode

The lesson: Context API exists for a reason. Use it for global state.


Mistake #5: I Didn't Think About Performance

What I did:

function TodoList({ todos }) {
  return (
    <div>
      {todos.map(todo => (
        <TodoItem 
          key={todo.id} 
          todo={todo}
          onDelete={() => deleteTodo(todo.id)}
          onEdit={() => editTodo(todo.id)}
        />
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Why it's slow: Creates new functions on every render. Every todo re-renders when ANY todo changes.

What I should've learned:

const TodoItem = memo(({ todo, onDelete, onEdit }) => {
  return <div>{todo.text}</div>;
});

function TodoList({ todos }) {
  const handleDelete = useCallback((id) => {
    deleteTodo(id);
  }, []);

  return (
    <div>
      {todos.map(todo => (
        <TodoItem 
          key={todo.id} 
          todo={todo}
          onDelete={handleDelete}
        />
      ))}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The lesson: Use memo, useMemo, useCallback for expensive operations.


What I Wish I Learned First

If I could restart learning React today, I'd learn in this order:

  1. Functional components + Hooks (skip class components entirely)
  2. Proper state management (useState → useReducer → Context)
  3. Data fetching (React Query or SWR, not raw useEffect)
  4. Performance optimization (memo, useMemo, useCallback)
  5. Component patterns (compound components, render props)

Resources That Actually Helped

Don't learn from:

  • Tutorials older than 2020
  • Courses teaching class components first
  • Random YouTube videos without dates

Learn from:

  • Official React docs (beta.reactjs.org)
  • Kent C. Dodds blog
  • React Query documentation
  • Josh Comeau's blog

The Real Lesson

You'll learn React wrong at first. Everyone does.

The key is unlearning bad patterns quickly.

Signs you learned React wrong:

  • Still using class components
  • useEffect for data fetching
  • Props drilling 5 levels deep
  • Creating functions inside render
  • Not using any state management library

Fix it now. Refactor one project using modern patterns.

Your future self (and code reviewers) will thank you.


What React mistake did you make? Drop it below. Let's learn together. 👇

Top comments (1)

Collapse
 
leob profile image
leob

Great summary/overview!