Back to Blog
My Journey with React Hooks: From Class Components to Modern React
ReactHooksJavaScriptFrontend

My Journey with React Hooks: From Class Components to Modern React

Discovering React Hooks transformed my development workflow. Learn about the practical benefits, common pitfalls, and why hooks make React code more maintainable and enjoyable to write.

Jan 02, 2025
7 min read
Sharath Devadiga

My Journey with React Hooks: From Class Components to Modern React

When I first started learning React, class components were the standard way to manage state and lifecycle methods. They seemed logical at the time—you have a class, methods, and state. But as I built more complex applications, I noticed something: my code was becoming verbose, difficult to test, and hard to maintain.

The Breaking Point

The moment I knew I had to learn hooks was during the development of my entertainment hub application. I had a component with over 15 different methods, nested this.setState calls, and a componentDidMount that was 50 lines long. The component was doing too much, and splitting it up seemed impossible because the logic was so tightly coupled.

Starting with useState: The First Revelation

The first hook I learned was useState, and honestly, it blew my mind. Compare these two approaches:

Before (Class Component):

javascript
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
      loading: false
    };
  }

  handleIncrement = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleIncrement}>Increment</button>
      </div>
    );
  }
}

After (Hooks):

javascript
function Counter() {
  const [count, setCount] = useState(0);
  const [loading, setLoading] = useState(false);

  const handleIncrement = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleIncrement}>Increment</button>
    </div>
  );
}

The difference was immediately clear. Less boilerplate, more readable, and I could focus on the logic rather than the ceremony of class components.

useEffect: Understanding the Lifecycle

Understanding useEffect took me longer. I kept thinking of it as componentDidMount, but it's so much more powerful. The dependency array was confusing at first, but once I understood it, I could control exactly when my effects ran.

Here's what I learned about useEffect:

Data Fetching:

javascript
useEffect(() => {
  const fetchData = async () => {
    setLoading(true);
    try {
      const response = await fetch('/api/data');
      const data = await response.json();
      setData(data);
    } catch (error) {
      setError(error.message);
    } finally {
      setLoading(false);
    }
  };

  fetchData();
}, []); // Empty dependency array means run once on mount

Cleanup:

javascript
useEffect(() => {
  const interval = setInterval(() => {
    setTime(new Date());
  }, 1000);

  return () => clearInterval(interval); // Cleanup function
}, []);

Custom Hooks: My First "Aha" Moment

Creating my first custom hook felt like magic. I had repetitive data fetching logic across multiple components, and suddenly, complex logic became reusable. Here's the custom hook I created:

javascript
function useApi(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        if (!response.ok) throw new Error('Failed to fetch');
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

I used this hook in 5 different components, eliminating hundreds of lines of duplicate code!

What I Wish I Knew Earlier

After working with hooks for over a year, here's what I wish someone had told me when I started:

1. Don't Overthink the Dependency Array

Include what you use, and let ESLint help you. The exhaustive-deps rule is your friend, not your enemy.

2. Custom Hooks Aren't Scary

If you're repeating logic across components, extract it into a custom hook. Start simple—even a hook that just manages a boolean state is valuable.

3. useState Doesn't Merge Objects

Unlike this.setState in class components, useState completely replaces the state. Use the spread operator or multiple state variables:

javascript
// Don't do this
const [user, setUser] = useState({ name: '', email: '' });
setUser({ name: 'John' }); // This loses the email!

// Do this instead
setUser(prev => ({ ...prev, name: 'John' }));

// Or use separate state variables
const [name, setName] = useState('');
const [email, setEmail] = useState('');

4. useCallback and useMemo Aren't Always Needed

Start simple, optimize later. These hooks are for performance optimization, not for every function or calculation.

The Real-World Impact

After switching to hooks, my code became cleaner, easier to test, and more enjoyable to write. My components went from 100+ line classes to 20-30 line functions. More importantly, my applications became more maintainable.

Here's a real example from my movie app:

Before: A 120-line MovieDetail class component with complex state management

After: A 35-line functional component with three custom hooks (useMovieDetails, useWatchlist, useRecommendations)

The hooks approach made each piece of logic focused and reusable. I could easily test each hook independently and reuse the logic in other components.

Tips for Learning Hooks

  • Start Small: Convert simple class components first
  • Use the React DevTools: The hooks inspector helps you understand what's happening
  • Practice Custom Hooks: They're the real superpower of hooks
  • Don't Rush: Take time to understand the mental model
  • Looking Forward

    React Hooks changed how I think about component design. Instead of thinking about lifecycle methods, I think about synchronizing with external systems. Instead of complex class hierarchies, I compose simple functions.

    If you're still using class components, I encourage you to try hooks. Start with a simple component, and I guarantee you'll be amazed at how much cleaner your code becomes.

    The learning curve is worth it, and once you understand hooks, you'll wonder how you ever lived without them.

    SD

    Sharath Devadiga

    Software Developer passionate about creating efficient, user-friendly web applications. Currently building projects with React, Node.js, and modern JavaScript technologies.