Hooks Pattern

Overview

Hooks are a feature introduced in React 16.8 that allow you to use state and other React features without writing a class. They enable you to reuse stateful logic between components, making your code more concise and easier to understand. Common built-in hooks include useState, useEffect, useContext, useRef, and more.

Example

Basic Hooks Usage

React Hooks Example

This example demonstrates basic React hooks: useState, useEffect, and useRef.

Count: 0

Ref Value: 0

Check the browser console to see the effects of different hooks.

  • useState manages the counter state
  • useRef keeps track of values between renders
  • useEffect runs side effects on different dependencies
  • The button gets focus when count is divisible by 5

Code Implementation

// HooksExample.tsx
import React, { useState, useEffect, useRef } from "react";

export const HooksExample: React.FC = () => {
  // useState - Managing state in a functional component
  const [count, setCount] = useState(0);
  const [isActive, setIsActive] = useState(false);
  
  // useRef - Persisting values across renders without causing re-renders
  const intervalRef = useRef<NodeJS.Timeout | null>(null);
  const buttonRef = useRef<HTMLButtonElement>(null);
  
  // useEffect - Side effects like API calls, DOM manipulation, subscriptions
  useEffect(() => {
    // This runs after every render
    document.title = `Count: ${count}`;
    
    // This cleanup function runs before the component unmounts
    // or before the effect runs again
    return () => {
      document.title = 'React App';
    };
  }, [count]); // Only re-run when count changes
  
  useEffect(() => {
    // Focus the button when the component mounts
    if (buttonRef.current) {
      buttonRef.current.focus();
    }
    
    // Clean up any intervals on unmount
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, []); // Empty dependency array means this runs once on mount
  
  // Toggle the counter
  const toggleCounter = () => {
    setIsActive(!isActive);
    
    if (!isActive) {
      // Start the interval
      intervalRef.current = setInterval(() => {
        setCount(c => c + 1);
      }, 1000);
    } else {
      // Stop the interval
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
        intervalRef.current = null;
      }
    }
  };
  
  // Reset the counter
  const resetCounter = () => {
    setCount(0);
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
    setIsActive(false);
  };

  return (
    <div className="flex flex-col items-center space-y-4">
      <div className="text-4xl font-bold">{count}</div>
      
      <div className="space-x-2">
        <button
          ref={buttonRef}
          onClick={toggleCounter}
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          {isActive ? 'Pause' : 'Start'}
        </button>
        
        <button
          onClick={resetCounter}
          className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600"
        >
          Reset
        </button>
      </div>
      
      <div className="text-sm text-gray-500">
        {isActive ? 'Counter is running' : 'Counter is paused'}
      </div>
    </div>
  );
};

Key Benefits

  • Allows using state and other React features in functional components
  • Improves code reusability through custom hooks
  • Reduces component complexity and nesting
  • Better organization of related logic in a single place
  • Easier to understand and maintain than class components with lifecycle methods
  • Enables better static analysis and tree-shaking by the bundler