Passing Functions

Passing a Function Through Context

Context carries behavior as happily as it carries data. Publishing a state setter — or, later, a dispatch function — lets a deeply nested component change state that lives near the root, with no callback prop in between.

A state setter delivered five levels downJavaScript
const CounterContext = createContext(null);
function Page() {
  const [count, setCount] = useState(0);
  return <CounterContext value={setCount}>
    <p>Counter: {count}</p><A />
  </CounterContext>;
}
const A = () => <B />, B = () => <C />, C = () => <D />, D = () => <Plus />;
function Plus() {
  const bump = useContext(CounterContext);     // any inner function can call it too
  return <button onClick={() => bump(n => n + 1)}>+1</button>;
}

A through D are pass-through components that never mention the counter, yet clicking the button three levels below them moves the number that Page owns.

This works cleanly for one reason worth remembering: the setter React 7,897 hands you from useState has a stable identity. It is the same function object on every render, so the context value never changes and no reader re-renders because of it. A handler you write yourself — value={() => setCount(c => c + 1)} — is a new function each render and does the opposite, forcing every reader to re-render whenever Page does; useCallback (useCallback) restores stability there. Publishing a raw setter also leaks how the state is stored. An object of named actions such as { increment, reset } reads better at the call site, and a reducer's dispatch is better still once the actions multiply.