Back to All Articles
reacthooksjavascriptfrontend

Mastering Modern React Hooks: A Practical Guide to the New Documentation

Learn how to build production apps using modern React hooks, function components, and interactive code examples from the new documentation.

2026-10-29
Mastering Modern React Hooks: A Practical Guide to the New Documentation

Let's look at how to stop writing legacy class components and fully adopt modern React hooks with the patterns from the official react.dev docs.

What you'll learn:

  • Why modern React dropped class components for hooks
  • How to structure state with useState and derived values
  • How to avoid common rendering pitfalls
  • How to use interactive sandboxes to test React APIs

Why the Shift to Function Components

For years, developers learned React twice: first with classes, then with hooks. That creates unnecessary overhead. The modern React ecosystem relies entirely on function components and hooks.

When you write components as pure functions, your UI becomes a direct projection of your state. Here is a simple counter hook example replacing old class state methods:

TSX
import { useState } from 'react';

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

  function handleClick() {
    setCount(prev => prev + 1);
  }

  return (
    <button onClick={handleClick}>
      Clicked {count} times
    </button>
  );
}

Speed Note: Always use the updater function pattern (setCount(prev => prev + 1)) when your next state depends on the previous state to avoid stale closures.

Managing Complex State with History

As apps grow, simple state is not enough. You often need to track history, like undo/redo stacks or game moves.

Here is how you manage an array of states using useState without mutating your original history arrays:

TSX
import { useState } from 'react';

export default function GameHistory() {
  const [history, setHistory] = useState([Array(9).fill(null)]);
  const [currentMove, setCurrentMove] = useState(0);
  
  const currentSquares = history[currentMove];
  const xIsNext = currentMove % 2 === 0;

  function handlePlay(nextSquares: (string | null)[]) {
    const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
    setHistory(nextHistory);
    setCurrentMove(nextHistory.length - 1);
  }

  return (
    <div className="p-4">
      <p className="font-bold">Current turn: {xIsNext ? 'X' : 'O'}</p>
      <p>Move #{currentMove}</p>
    </div>
  );
}

Gotcha: Never mutate state directly (squares[i] = 'X'). Always slice or spread arrays to create new references so React knows when to trigger a re-render.

Thinking in Components and Props

Breaking down a complex user interface starts with identifying boundaries. Instead of massive monolithic components, break your UI into single-responsibility pieces.

Here is how you separate a reusable Square component from its parent Board:

TSX
import React from 'react';

interface SquareProps {
  value: string | null;
  onSquareClick: () => void;
}

export function Square({ value, onSquareClick }: SquareProps) {
  return (
    <button 
      className="h-12 w-12 border border-gray-300 text-xl font-bold flex items-center justify-center bg-white hover:bg-gray-100"
      onClick={onSquareClick}
    >
      {value}
    </button>
  );
}

Accessibility: Ensure buttons containing only symbols or icons include an aria-label attribute so screen readers can describe their action accurately.

Leveraging Built-in API References

When building custom components, don't guess API signatures. The new API Reference section on react.dev splits documentation into two clear segments: Reference (formal signatures and return values) and Usage (real-world recipes).

Whenever you reach for a hook like useState, useEffect, or useMemo, check the interactive examples to see canonical use cases before writing custom abstractions.

TSX
import { useState, useMemo } from 'react';

export default function FilteredList({ items }: { items: string[] }) {
  const [query, setQuery] = useState('');

  const filteredItems = useMemo(() => {
    return items.filter(item => item.toLowerCase().includes(query.toLowerCase()));
  }, [items, query]);

  return (
    <div>
      <input 
        value={query} 
        onChange={e => setQuery(e.target.value)} 
        placeholder="Filter items..."
        className="border p-2 rounded"
      />
      <ul className="mt-2">
        {filteredItems.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    </div>
  );
}

Summary

Migrating your mental model to modern React means fully embracing function components, predictable state management, and clear component boundaries. Using the interactive examples and reference guides on react.dev will speed up your daily development workflow.

Next Steps:

  • Explore the interactive challenges in the "Learn React" section of react.dev.
  • Audit your existing codebase for legacy class components and refactor them to hooks.
  • Bookmark the API Reference pages for quick syntax lookups during feature builds.