React Web Workers: The Thread Doesn't Render, It Just Ships Results Back
Offloading CPU work to a Worker keeps a React UI responsive, but the real risk is architectural: storing a Worker in component state or creating it on every render leaks threads and breaks the message contract. This pattern pins down exactly which hook owns which responsibility.
Running a 5-billion-iteration loop in a React component freezes the UI because the Event Loop won't parallelize synchronous work. The fix is a Web Worker, but the architecture matters more than the thread itself. The Worker instance lives in a `useRef` — not state, since it never drives rendering — and gets created inside a `useEffect` that also handles `terminate()` on cleanup. A two-way `postMessage` protocol sends a task payload from main thread to Worker, and the Worker ships a plain result object back; the main thread then calls `setState` to update the UI. The Worker never touches the DOM or React components, so rendering stays firmly on the main thread.
Static code review surfaces four bugs worth checking at runtime: the UI says 500 million iterations but the loop runs 5 billion, the result can overflow JavaScript's safe integer range, there's no `onerror` handler to clear the loading state on failure, and a falsy result of `0` won't render under a truthiness guard. A decision flowchart — CPU-bound? Pure computation? — helps decide when this pattern applies.
The pattern's value isn't multi-threading — it's treating a Worker as an external resource with explicit creation, messaging, and teardown, the same way you'd manage a database connection.
Storing the Worker in `useRef` rather than state is a deliberate signal: this object exists outside React's render cycle and should never trigger a re-render.
The static code review catches four concrete bugs without running the code, which is a useful reminder that reading source can surface integer-overflow, truthiness, and error-handling gaps before a browser does.
The decision flowchart — CPU-bound? Pure computation? — is a practical filter that prevents developers from over-applying Workers to tasks the Event Loop already handles well.