MeshWorld India Logo MeshWorld.
react hooks typescript frontend state-management 5 min read

React Hooks & Custom Hooks Cheatsheet: The Complete Reference

Scarlett
By Scarlett
React Hooks & Custom Hooks Cheatsheet: The Complete Reference

React Hooks revolutionized frontend development by enabling state and other lifecycle capabilities inside functional components. Understanding hooks in depth allows you to manage reactive state, minimize unnecessary re-renders, and abstract complex behaviors into clean, reusable modules.

This reference sheet covers standard React hooks, performance optimization structures, and provides a robust, production-ready custom hook implementation.


  • State & Lifecycle: Leverage useState and useEffect to manage reactive data and side-effect behaviors.
  • Reference Management: Use useRef to store persistent values that do not trigger re-renders, or target DOM nodes directly.
  • Performance Tuning: Apply useMemo and useCallback to cache expensive calculations and prevent unnecessary function redeclarations.
  • Context API: Consume shared state across deep component trees using useContext without prop-drilling.
  • Custom Hooks: Abstract reusable logic into custom functions prefixed with use to encapsulate state operations.

Before diving into this cheatsheet, check out my previous deep-dive on React Hooks Cheat Sheet: All Hooks with React 19 to see how we structured these patterns in practice.

Core Hooks

1. State Management (useState)

useState declares a state variable that triggers a component re-render when modified.

import { useState } from 'react';

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

  // Use functional updates when new state depends on previous state
  const increment = () => setCount((prevCount) => prevCount + 1);

  return (
    <button onClick={increment}>
      Count: {count}
    </button>
  );
}

2. Side-Effects Management (useEffect)

useEffect executes side-effects (fetching data, setting up subscriptions, DOM manipulation) in functional components.

import { useEffect, useState } from 'react';

function WindowWidthTracker() {
  const [width, setWidth] = useState<number>(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    
    // Add event listener
    window.addEventListener('resize', handleResize);
    
    // Clean up function executed before running the effect again or unmounting
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  }, []); // Empty dependency array means this runs only on mount and unmount

  return <p>Window Width: {width}px</p>;
}

Optimizing Performance

React provides hooks specifically designed to cache computations or function references between renders, which is useful when passing callbacks to optimized child elements.

1. Caching Computations (useMemo)

useMemo caches the calculated result of an expensive operation.

import { useMemo } from 'react';

function ProductList({ products, filterQuery }) {
  // Only re-runs search filter if products or filterQuery change
  const filteredProducts = useMemo(() => {
    console.log('Filtering products...');
    return products.filter((p) => p.name.toLowerCase().includes(filterQuery.toLowerCase()));
  }, [products, filterQuery]);

  return (
    <ul>
      {filteredProducts.map((p) => (
        <li key={p.id}>{p.name}</li>
      ))}
    </ul>
  );
}

2. Caching Function References (useCallback)

useCallback caches a function definition itself to prevent redeclaration on every render pass.

import { useState, useCallback } from 'react';

function ParentComponent() {
  const [state, setState] = useState(0);

  // Caches this callback so ChildComponent (if wrapped in React.memo) does not re-render
  const handleClick = useCallback(() => {
    console.log('Button clicked');
  }, []); // Caches once on mount

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

Reference & DOM Manipulation (useRef)

useRef persists a mutable value across renders without triggering a new render cycle. It is also used to reference physical DOM nodes directly.

import { useRef, useEffect } from 'react';

function AutoFocusInput() {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    // Focus the input element on component mount
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} type="text" placeholder="Type here..." />;
}

Designing a Reusable Custom Hook

Custom hooks allow you to package component logic into reusable functions. Below is a robust, production-grade custom hook to synchronize state to Browser localStorage with error handling.

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

export function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((val: T) => T)) => void] {
  // Read value from localStorage or fallback to initialValue
  const readValue = useCallback((): T => {
    if (typeof window === 'undefined') {
      return initialValue;
    }

    try {
      const item = window.localStorage.getItem(key);
      return item ? (JSON.parse(item) as T) : initialValue;
    } catch (error) {
      console.warn(`Error reading localStorage key "${key}":`, error);
      return initialValue;
    }
  }, [key, initialValue]);

  // Initialize state with read value
  const [storedValue, setStoredValue] = useState<T>(readValue);

  // Return a memorized setter function
  const setValue = useCallback((value: T | ((val: T) => T)) => {
    try {
      // Allow value to be a function so we have the same API as useState
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      
      // Update state
      setStoredValue(valueToStore);
      
      // Update localStorage
      if (typeof window !== 'undefined') {
        window.localStorage.setItem(key, JSON.stringify(valueToStore));
      }
    } catch (error) {
      console.warn(`Error setting localStorage key "${key}":`, error);
    }
  }, [key, storedValue]);

  // Synchronize state across multiple browser tabs
  useEffect(() => {
    const handleStorageChange = (e: StorageEvent) => {
      if (e.key === key && e.newValue) {
        try {
          setStoredValue(JSON.parse(e.newValue) as T);
        } catch (error) {
          console.warn(`Error parsing storage sync event for key "${key}":`, error);
        }
      }
    };

    window.addEventListener('storage', handleStorageChange);
    return () => window.removeEventListener('storage', handleStorageChange);
  }, [key]);

  return [storedValue, setValue];
}

Consuming the Custom Hook

Here is how you would use useLocalStorage to save a user preferences object in a React component:

import { useLocalStorage } from './useLocalStorage';

interface Preferences {
  theme: 'light' | 'dark';
  notificationsEnabled: boolean;
}

function SettingsForm() {
  const [preferences, setPreferences] = useLocalStorage<Preferences>('user-preferences', {
    theme: 'dark',
    notificationsEnabled: true,
  });

  const toggleTheme = () => {
    setPreferences((prev) => ({
      ...prev,
      theme: prev.theme === 'dark' ? 'light' : 'dark',
    }));
  };

  return (
    <div className={`app-${preferences.theme}`}>
      <button onClick={toggleTheme}>
        Toggle Theme (Current: {preferences.theme})
      </button>
    </div>
  );
}