React Hooks Guide

Introduction to Hooks

Hooks are functions that let you "hook into" React state and lifecycle features from function components. Introduced in React 16.8, they allow you to use state and other React features without writing classes.

Why Use Hooks?

  • Simplify complex components
  • Reuse stateful logic without classes
  • Organize related logic into single hook
  • Reduce boilerplate code
  • Easier to test and maintain

Basic Hooks

useState

Manages state in functional components. Returns a stateful value and a function to update it.

import { 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>
  );
}

useEffect

Performs side effects in function components (data fetching, subscriptions, DOM manipulation).

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(res => res.json())
      .then(data => setData(data));

    return () => {
      // Cleanup function (runs on unmount)
    };
  }, []); // Empty array means run once on mount

  return <div>{data ? data.message : 'Loading...'}</div>;
}

useContext

Accesses context values without nesting consumers.

import { createContext, useContext } from 'react';

const ThemeContext = createContext('light');

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>I'm styled by theme!</button>;
}

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

Additional Hooks

useReducer

Alternative to useState for complex state logic

const [state, dispatch] = useReducer(reducer, initialState);

useCallback

Memoizes functions to prevent unnecessary re-renders

const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);

useMemo

Memoizes expensive calculations

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

useRef

Creates mutable ref object that persists

const inputRef = useRef(null);
<input ref={inputRef} />

useImperativeHandle

Customizes instance value exposed to parent components

useImperativeHandle(ref, () => ({ focus: () => { /* ... */ } }));

useLayoutEffect

Same as useEffect but fires synchronously after DOM mutations

useLayoutEffect(() => { /* measure DOM */ });

useDebugValue

Displays custom labels in React DevTools

useDebugValue(isOnline ? 'Online' : 'Offline');

Custom Hooks

You can create your own hooks to extract component logic into reusable functions.

Example: useFetch

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
}

// Usage:
function MyComponent() {
  const { data, loading, error } = useFetch('https://api.example.com/data');
  // ...
}

Hooks Best Practices

Call Hooks at the Top Level

Don't call hooks inside loops, conditions, or nested functions

Only Call Hooks from React Functions

Call them from React components or custom hooks

Use Multiple Effects

Separate concerns by using multiple useEffect hooks

Optimize Dependencies

Include all dependencies used in your effect

Memoize Expensive Operations

Use useMemo/useCallback for performance optimization

Clean Up Effects

Always return cleanup functions when needed