React Hooks 101 & Beyond: How to Build Your Own Hooks

VANSH KANSALVANSH KANSAL
5 min read

React is one of the most popular JavaScript libraries for building user interfaces. Before 2018, React components were divided into two camps: class components (which could hold state and lifecycle methods) and functional components (which couldn’t).

Hooks changed everything.

Introduced in React 16.8, Hooks let you use state and other React features inside functional components—without writing a class.


What Are Hooks?

Hooks are not more than just JavaScript functions that let you “hook into” React features like state, lifecycle, and context.

They look like regular functions, but they follow special rules and naming conventions (e.g., their names start with use as per convention).

Let’s walk through the most commonly used hooks and see how they work.


1.) useState: Manage Component State

Definition:
useState lets you add state (data that changes over time) to functional components.

How it works:

  • You call useState inside your component to create a state variable.

  • React preserves this state between re-renders.

  • When you update state, React re-renders the component.

Where it’s mainly used:

  • Handling form inputs

  • Toggling UI (like modals or dropdowns)

  • Storing fetched data

Syntax:

const [state, setState] = useState(initialValue);
//here state is the value
//setState is function that will store value in state

Example:

import { useState } from "react";

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

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

Every time you click the button, count increases and the component re-renders.


2.) useEffect: Side Effects and Lifecycle

Definition:
useEffect lets you perform side effects in your component—like fetching data, updating the DOM, or subscribing to events.

How it works:

  • useEffect runs after every render by default.

  • You can control when it runs by passing a dependency array.

  • It can also return a cleanup function to run when the component unmounts or dependencies change.

Where it’s mainly used:

  • Data fetching on mount

  • Listening to window events

  • Subscribing/unsubscribing to external services

Syntax:

useEffect(() => {
  // code to run on mount/update
  return () => {
    // cleanup
  };
}, [dependencies]);

Example:

import { useEffect, useState } from "react";

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

  useEffect(() => {
    const interval = setInterval(() => {
      setCount(c => c + 1);
    }, 1000);

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

  return <p>Timer: {count}</p>;
}

This sets up a timer when the component mounts and cleans it up when it unmounts.


3.) useRef: Mutable References

Definition:
useRef lets you persist a mutable value that doesn’t trigger a re-render when it changes.

How it works:

  • Returns an object with a .current property.

  • Updating .current does not cause the component to re-render.

  • Commonly used to access DOM nodes or store mutable values.

Where it’s mainly used:

  • Accessing DOM elements (like input focus)

  • Storing timers or intervals

  • Keeping a mutable value across renders without causing re-render

Syntax:

const myRef = useRef(initialValue);

Example:

import { useRef, useEffect } from "react";

function InputFocus() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} placeholder="I will be focused" />;
}
//Try this out

Note: Updating useRef doesn’t trigger a re-render, while updating useState does.


4.) useContext: Share State Globally

Definition:
useContext lets you read and subscribe to context (shared state) in your component without passing props manually at every level.

How it works:

  • You create a context with React.createContext.

  • Use a Provider to pass the context value.

  • useContext consumes the value anywhere in the component tree.

Where it’s mainly used:

  • Global themes or user data

  • Authentication status

  • Multi-language (i18n) support

Syntax:

const value = useContext(MyContext);

Example:

import { createContext, useContext } from "react";

const ThemeContext = createContext("light");

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button>{theme === "dark" ? "Dark Mode" : "Light Mode"}</button>;
}

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedButton />
    </ThemeContext.Provider>
  );
}

The button will show Dark Mode because it reads the context value.


Note on useMemo and useCallback

useMemo and useCallback are used to memoize values and functions to avoid unnecessary re-computations or re-creations between renders. However, starting React 19, their usage is significantly reduced because React’s compiler can optimize re-renders more intelligently.

In most cases, you may no longer need to wrap everything in useMemo or useCallback—focus on real performance bottlenecks instead.


While developing in React, you’ll find yourself mostly juggling between useState and useEffect, as these two hooks cover the majority of day-to-day component logic. If you’d like to explore more advanced hooks, you can always refer to the official React documentation for a completet list and detailed usage examples.


How to Create a Custom Hook

Custom hooks are just functions that start with use and can call other hooks internally. They help you encapsulate and reuse logic.

Example:

import { useState, useEffect } from "react";

function useFetch(url) {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url)
      .then(res => res.json())
      .then(setData);
  }, [url]);

  return data;
}

Usage:

function Posts() {
  const posts = useFetch("https://website.com/value");

  if (!posts) return <p>Loading...</p>;

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Custom hooks make your components cleaner and easier to maintain.


TL;DR

React Hooks have transformed how developers write components, bringing state, side effects, and shared logic into functional components in a simple, elegant way. Whether you’re managing state with useState, performing side effects with useEffect, or encapsulating logic with custom hooks, mastering Hooks is essential for modern React development.

10
Subscribe to my newsletter

Read articles from VANSH KANSAL directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

VANSH KANSAL
VANSH KANSAL