Pure Render

Write render logic as a pure function of props and state; never trigger side‑effects during render.

Why?  Ensures compatibility with strict / concurrent rendering modes.

React calls your component’s render function a lot—sometimes twice in development (Strict Mode) and, with Concurrent Rendering, potentially even more. If that render kicks off a network request, mutates global data, or directly manipulates the DOM, you’ll get duplicate calls, race conditions, or even infinite loops. Beginners often sprinkle “just one fetch” or localStorage.setItem right inside render, unaware that React expects render to be a pure calculation: same inputs → same output → zero side‑effects.

Correct Example

import { useEffect, useState } from "react";

export default function RandomJoke() {
  const [joke, setJoke] = useState<string | null>(null);

  // Side‑effect (network fetch) lives in useEffect, not render.
  useEffect(() => {
    fetch("https://api.chucknorris.io/jokes/random")
      .then((res) => res.json())
      .then((data) => setJoke(data.value));
  }, []); // run once after initial mount

  // Render stays pure—just reflects current state.
  return (
    <section className="rounded-xl border p-4">
      {joke ?? "Loading a hilarious joke…"}
    </section>
  );
}

What’s happening? Render merely transforms joke state into UI. All side‑effects (fetch) are quarantined in useEffect, ensuring no duplicate requests on re‑render.

Incorrect Example

function RandomJokeImpure() {
  const [joke, setJoke] = useState<string | null>(null);

  // ❌ Fetching during render—runs every render in Dev Strict Mode.
  if (!joke) {
    fetch("https://api.chucknorris.io/jokes/random")
      .then((res) => res.json())
      .then((data) => setJoke(data.value));
  }

  return (
    <section className="rounded-xl border p-4">
      {joke ?? "Loading a hilarious joke…"}
    </section>
  );
}
// In development, Strict Mode double‑invokes render → two fetches. In
// concurrent mode, React may start and abandon renders, leaking requests.

Key Takeaways

  • Render = math, nothing else: Treat it like a pure function—no mutations, I/O, or timers.
  • Side‑effects live in Hooks: Use useEffect (or useLayoutEffect for layout‑critical work) to perform asynchronous calls, subscriptions, or DOM writes.
  • Avoid state‑updates‑inside‑render: Calling setState during render triggers an immediate re‑render loop.
  • Strict Mode will punish impurity: React dev tooling intentionally double‑calls render to surface hidden side‑effects—embrace it as a safety net.
  • Purity unlocks concurrency: Clean render functions let React pause, resume, or replay work without causing logical fallout.