Upgrading to React 18: createRoot, StrictMode, and the Double-Effect Surprise
React 18 shipped this week. The mechanical upgrade is genuinely two lines, but the behavioral changes deserve attention before you flip the switch on a large app.
The two lines
// Before
import { render } from "react-dom";
render(<App />, document.getElementById("root"));
// After
import { createRoot } from "react-dom/client";
createRoot(document.getElementById("root")).render(<App />);
Keeping the old render API works but opts you out of every React 18 feature — concurrent rendering, automatic batching, transitions. Don't half-upgrade.
Automatic batching (a quiet perf win)
Pre-18, multiple setState calls batched only inside React event handlers. In promises, timeouts, and native handlers, each caused its own render:
fetch("/api/user").then(() => {
setUser(u); // React 17: render
setLoading(false); // React 17: render again
}); // React 18: one render, batched
Most apps get faster for free. If you truly need a synchronous commit between updates, flushSync exists — treat it as an escape hatch with a code comment explaining why.
StrictMode now double-invokes effects
The one that generates the bug reports: in development, StrictMode mounts, unmounts, and remounts every component — so useEffect runs, cleans up, and runs again. Effects written without cleanup break visibly:
useEffect(() => {
const ws = new WebSocket(url); // now opens twice in dev
return () => ws.close(); // ...unless you clean up properly
}, [url]);
This isn't React being annoying — it's simulating the reusable-state future (preserving state when components unmount/remount, e.g. back navigation). Every effect that breaks under double-invocation was already subtly wrong. We found four real leaks in our codebase this way.
Upgrade order that worked for us
- Upgrade react/react-dom, keep legacy
render— confirm nothing breaks. - Switch to
createRootwith StrictMode off — check batching-sensitive code. - Enable StrictMode, fix every effect that misbehaves.
- Then start adopting
useTransitionandSuspensefeatures deliberately.