`useEffect` runs after render for side effects: subscriptions, timers, fetches. Returning a function from the effect cleans it up before the next run or on unmount.
import { useEffect, useState } from "react";
function usePrice(symbol: string) {
const [price, setPrice] = useState<number | null>(null);
useEffect(() => {
let cancelled = false;
fetchPrice(symbol).then((p) => { if (!cancelled) setPrice(p); });
return () => { cancelled = true; };
}, [symbol]);
return price;
}The dependency array controls when the effect re-runs: omitting it runs on every render, `[]` runs once on mount, and `[symbol]` re-runs whenever `symbol` changes.