React Hooks – The Process of Using React Hooks

React Hooks are functions that let you use state and other React features without writing a class. They were introduced in React 16.8 to simplify state management and side effects in functional components. Core React

Written by: Zakaria

Published on: July 7, 2025

React Hooks are functions that let you use state and other React features without writing a class. They were introduced in React 16.8 to simplify state management and side effects in functional components.

Core React Hooks Explained

useState: Managing State in Functional Components

So, you’re juggling multiple tasks, maybe sipping coffee, and trying to keep track of a counter in your app. That’s where
useState comes in. It’s like having a sticky note that updates itself. You declare a state variable and a function
to update it. Every time you call the updater, React re-renders the component with the new state. Simple, right? It’s perfect
for handling form inputs, toggles, or any value that changes over time.

  • Easy to implement — no classes, just functions and clean code.
  • Useful in forms — name, email, checkbox states, all covered.
  • Supports multiple state variables — not limited to one per component.
  • Pairs well with other hooks like useEffect for more dynamic behavior.
  • Encourages functional, stateless design — simpler to debug.

And the syntax? Barebones. Short. You’ll probably memorize it accidentally after using it twice. No setup drama. Just:

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

Yeah, that’s it. Then just update setCount whenever something changes. Done. No lifecycle methods to worry about.
React just… handles it. Feels like cheating, kind of.

 

The useState hook allows you to add state to functional components.

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Each time the button is clicked, the count increases by one.

useEffect: Handling Side Effects

Mid-scroll through emails, you’re also building a dashboard and — wait, did you forget the API call? Yep. This is where
useEffect swoops in. Whether it’s fetching data, syncing with localStorage, setting up a listener, or doing
something DOM-ish like changing the title bar — these are all side effects.

useEffect kicks in after your component renders. You don’t block the UI — React renders first, then handles your
background stuff. You also get to clean things up before it runs again or before the component unmounts. So no memory leaks,
hopefully.

  • Runs automatically after render — no need to manually trigger anything.
  • Dependency array keeps things efficient — it only reruns when needed.
  • Perfect for fetching data, event listeners, subscriptions, timers… whatever’s not pure UI.
  • Cleanup function avoids ghosts from past renders — ideal for removing event listeners or timers.

useEffect(() => {
  const interval = setInterval(() => {
    console.log('Tick');
  }, 1000);

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

Feels a bit like setting a reminder in your app that keeps repeating until you tell it to stop. Quietly powerful. Also,
depending on how you use the dependency array — it might fire once, or every time something changes. Dangerous? Nah.
Just precise. 

The useEffect hook lets you perform side effects in function components, such as data fetching or manual DOM manipulations.

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

function Timer() {
  const [seconds, setSeconds] = useState(0);
  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);
  return <p>Timer: {seconds}s</p>;
}

This sets up a timer that increments every second and cleans up on unmount.

useReducer: Managing Complex State Logic

You start with one input field, then add a checkbox, then a multi-step form — and suddenly your useState setup
looks like spaghetti. That’s when useReducer steps in, calmly. It’s built for when state gets… layered.

It’s like Redux-lite. You write a reducer function that takes the current state and an action, then returns a new state.
React keeps it all neat and predictable. No more tangled update logic across multiple setState calls.

  • Cleaner logic for complex state — especially when one update depends on the previous one.
  • Organized — actions and state are centralized, readable.
  • Good for multi-field forms, modals, toggles, and anything with steps or flows.
  • Can be extended easily — works with middleware or external state management too.

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'reset':
      return { count: 0 };
    default:
      return state;
  }
};

const [state, dispatch] = useReducer(reducer, { count: 0 });
  

You dispatch actions instead of calling setters directly. It feels a bit more structured — like there’s a process.
Kind of nice when your logic’s getting messy and you’ve got tabs open everywhere, half-paying attention.
useReducer just helps things behave. 

The useReducer hook is an alternative to useState for managing complex state logic.

import React, { useReducer } from 'react';

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
    </div>
  );
}

Ideal for managing state transitions in a predictable way.

useCallback: Memoizing Functions

Okay, so you’re passing a function to a child component… again. And somehow, things keep re-rendering when they shouldn’t.
That’s where useCallback saves your sanity. It memorizes your function so React doesn’t rebuild it on every render.
Which sounds minor until you’ve got a component re-rendering 37 times just because a prop function changed… slightly.

useCallback takes a function and an array of dependencies. If none of the dependencies change, the function
reference stays the same. So, your deeply nested child component that depends on that prop? It chills.

  • Reduces unnecessary re-renders in child components.
  • Useful when passing callbacks to memoized components (like with React.memo).
  • Keeps function references stable across renders.
  • Especially helpful with expensive operations wrapped in useEffect or useMemo.

const handleClick = useCallback(() => {
  doSomething();
}, [dependency]);
  

Is it overkill sometimes? Yeah. But in large apps or performance-heavy components, it’s a subtle win.
Quiet optimization. Less drama. You’ll hardly notice it — which is kind of the point. 

The useCallback hook returns a memoized version of the callback that only changes if one of the dependencies has changed.

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

function ExpensiveComponent({ onClick }) {
  // ...expensive rendering
}

function ParentComponent() {
  const [count, setCount] = useState(0);
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return <ExpensiveComponent onClick={handleClick} />;
}

Helps prevent unnecessary re-renders of child components.

Comparison Table

Hook Purpose Common Use Case
useState Manage state in functional components Form inputs, toggles
useEffect Handle side effects Data fetching, subscriptions
useContext Access context values Theming, user authentication
useRef Persist values across renders Accessing DOM elements
useReducer Manage complex state logic State transitions, forms
useCallback Memoize functions Optimizing child component renders

FAQs

When should I use useReducer over useState?
Use useReducer when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.
Can I use multiple hooks in a single component?
Yes, you can use multiple hooks in a single component. Just ensure they are called in the same order on every render.
What is the difference between useEffect and useLayoutEffect?
useEffect runs after the render is committed to the screen, whereas useLayoutEffect runs synchronously after all DOM mutations but before the browser has painted.

 

Leave a Comment

Previous

Understanding RESTful APIs with NodeJS

Next

Best Free React Web Projects for Beginners and Pros