useRef: The Escape Hatch
The useRef Hook serves two primary purposes:
1. Accessing DOM Elements: This is its most common use case. It provides a way to get a direct reference to a DOM element rendered by your component. This is useful for managing focus, triggering animations, or integrating with third-party DOM libraries.
JavaScript
import React, { useRef, useEffect } from 'react';
function TextInputWithFocus() {
  // Create a ref object
  const inputRef = useRef(null);
  // Use useEffect to focus the input when the component mounts
  useEffect(() => {
    // The actual DOM node is available on the .current property
    inputRef.current.focus();
  }, []); // Empty dependency array means this runs only once
  return <input ref={inputRef} type="text" />;
}
2. Storing a Mutable Value that Doesn't Cause a Re-render: Unlike useState, updating a ref's .current property does not trigger a re-render. This makes useRef perfect for storing information that needs to persist across renders but doesn't directly affect the visual output, like a timer ID or a previous state value.
JavaScript
const timerIdRef = useRef(null);
const startTimer = () => {
  timerIdRef.current = setInterval(() => { ... });
};
const stopTimer = () => {
  clearInterval(timerIdRef.current);
};
Performance Hooks: useMemo and useCallback
These hooks are tools for optimization. You should not use them everywhere, only when you have a measurable performance problem.
useMemo: Memoizing Values The useMemo Hook is for memoizing the result of an expensive calculation. It takes a function and a dependency array. React will only re-run the function (and re-calculate the value) if one of the dependencies has changed.
Imagine a function that filters a huge list, which is a slow operation:
JavaScript
import React, { useState, useMemo } from 'react';
function TodoList({ todos, filter }) {
  // This expensive calculation runs on every single render
  // const visibleTodos = filterTodos(todos, filter);
  // With useMemo, filterTodos will only be called again
  // if 'todos' or 'filter' changes.
  const visibleTodos = useMemo(() => {
    console.log("Running expensive filtering...");
    return filterTodos(todos, filter);
  }, [todos, filter]);
  return (
    <ul>
      {visibleTodos.map(todo => <li key={todo.id}>{todo.text}</li>)}
    </ul>
  );
}
If the component re-renders for another reason (e.g., a parent's state changes), useMemo will return the previously calculated visibleTodos array without running the expensive filtering logic again.
useCallback: Memoizing Functions The useCallback Hook is very similar, but instead of memoizing a value, it memoizes a function. This is useful when you are passing callbacks to child components that are optimized to only re-render when their props change.
JavaScript
import React, { useState, useCallback } from 'react';
import MyButton from './MyButton'; // Assume MyButton is a memoized component
function ParentComponent() {
  const [count, setCount] = useState(0);
  // Without useCallback, a new handleClick function is created on every render.
  // This would cause MyButton to re-render even if it's memoized.
  // const handleClick = () => {
  //   console.log("Button clicked!");
  // };
  // With useCallback, the same function instance is returned as long
  // as its dependencies (none, in this case) don't change.
  const handleClick = useCallback(() => {
    console.log("Button clicked!");
  }, []); // Empty dependency array
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
      <MyButton onClick={handleClick} />
    </div>
  );
}
By wrapping handleClick in useCallback, we ensure that the MyButton component receives the exact same function prop on every render, preventing unnecessary re-renders.