Skip to main content

Command Palette

Search for a command to run...

Understanding React Hooks: A Beginner's Introduction

Published
6 min read
Understanding React Hooks: A Beginner's Introduction

When React 16.8 was released, it introduced something called "Hooks" that everyone on Twitter seemed incredibly excited about. As someone still learning React and trying to wrap my head around all its concepts, I wanted to understand what all the fuss was about and why this feature matters for beginners like me.

What Are Hooks, Really?

Simply put, Hooks are functions that let you use state and other React features in functional components. Before Hooks, if you wanted to use state or lifecycle methods, you had to write class components with all that confusing this binding stuff. Now, you can do everything with simple functions, which feels much more natural to me as a beginner.

The React team has created several built-in Hooks, but the two most important ones to start with are useState and useEffect. Let me walk you through what I've learned about each of them.

My First Hook: useState

The first Hook I learned was useState, and honestly, it was a game-changer for how I think about React components. Here's the basic counter example that finally made it click for me:

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

See Demo 1 above for a working version!

Let me break down what's happening here:

  1. useState(0) creates a state variable with an initial value of 0

  2. It returns an array with two things: the current value (count) and a function to update it (setCount)

  3. We use array destructuring to grab both of these values

  4. When the button is clicked, setCount updates the value and React re-renders the component

This was eye-opening for me! Instead of writing a class with this.state and this.setState, I can just call useState and get back exactly what I need.

Handling Form Inputs

One of the most practical uses of useState I've found is handling form inputs. Here's a simple example:

function ContactForm() {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Submitted:', { name, email });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Your name"
      />
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Your email"
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Try Demo 2 above to see live form handling!

The pattern here is simple: the input's value is controlled by state, and the onChange handler updates that state. This is called a "controlled component" and it gives you complete control over the input's behavior.

You can use multiple useState hooks in a single component - each piece of state is completely independent. Updating name doesn't affect email, which makes your code much easier to reason about than having one big state object.

My Introduction to useEffect

After getting comfortable with useState, I started exploring useEffect. This Hook is for handling "side effects" - things like fetching data, setting up subscriptions, or manually changing the DOM.

Here's a simple timer I built to understand how it works:

import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);
  const [isRunning, setIsRunning] = useState(false);

  useEffect(() => {
    let interval;

    if (isRunning) {
      interval = setInterval(() => {
        setSeconds(s => s + 1);
      }, 1000);
    }

    // Cleanup function
    return () => {
      if (interval) {
        clearInterval(interval);
      }
    };
  }, [isRunning]); // Only re-run if isRunning changes

  return (
    <div>
      <h2>{seconds} seconds</h2>
      <button onClick={() => setIsRunning(!isRunning)}>
        {isRunning ? 'Pause' : 'Start'}
      </button>
      <button onClick={() => {
        setSeconds(0);
        setIsRunning(false);
      }}>
        Reset
      </button>
    </div>
  );
}

See Demo 3 for the working timer!

There's a lot happening here, so let me explain:

  1. useEffect takes a function that contains your side-effect code

  2. The second argument [isRunning] is a dependency array - the effect only runs when isRunning changes

  3. The cleanup function (the returned function) runs before the component unmounts or before the effect runs again

This was tricky for me at first, but once I understood that useEffect runs after the component renders, it started making sense.

Why This Matters for Beginners

As someone still learning React, I can already see why Hooks are such a big deal:

1. Less Code to Write
No more class components means no more constructor functions, no more this.setState, and no more worrying about binding methods.

2. Easier to Understand
Functional components with Hooks feel more like regular JavaScript. You're just calling functions and using variables.

3. Better Code Organization
Instead of scattering related logic across different lifecycle methods, you can group related functionality together in custom Hooks (which I'm still learning about).

4. Simpler Mental Model
I don't have to think about component lifecycle as much. I just think: "When this value changes, do this thing."

Comparing: Class vs Hooks

To really see the difference, here's the same counter component written both ways:

With Class Components (the old way):

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

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

With Hooks (the new way):

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

The Hooks version is shorter, easier to read, and I don't have to remember all the class syntax rules!

Common Mistakes I Made (So You Don't Have To)

1. Forgetting to import Hooks
Make sure you import useState and useEffect from React:

import React, { useState, useEffect } from 'react';

2. Calling Hooks inside conditions
This won't work:

// ❌ Wrong
if (something) {
  const [state, setState] = useState(0);
}

Hooks must always be called at the top level of your function.

3. Forgetting the dependency array in useEffect
Without it, your effect runs after every render, which can cause performance issues or infinite loops.

4. Not using the functional update form
When updating state based on the previous value, use the function form:

// ✅ Better
setCount(prevCount => prevCount + 1);

// Instead of
setCount(count + 1);

This prevents bugs when multiple updates happen quickly.

What I'm Learning Next

I'm still very much a beginner, but here's what I'm planning to explore next:

  • Custom Hooks: Creating my own reusable Hooks

  • useContext: For sharing state across components without prop drilling

  • useReducer: For more complex state management

  • Rules of Hooks: Understanding the technical details behind the scenes

Final Thoughts

If you're just starting out with learning React, I'd strongly recommend starting with Hooks from day one. They're officially part of React now, and the React team has said they're not going away. Classes aren't being removed, but Hooks are clearly the future direction of React.

The best part? You can adopt Hooks gradually. You can have class components and functional components with Hooks living side-by-side in the same app. There's no pressure to rewrite everything at once.

For me, Hooks have made React feel less intimidating and more approachable. Instead of feeling like I need to master classes, lifecycle methods, and this binding before I can be productive, I can start building useful components on day one.

If you want to learn more, I highly recommend the official React documentation on Hooks.