跪拜 Guibai
← All articles
Frontend · JavaScript

Keep React Responsive During Heavy Computation with Web Workers and useRef

By 渣波 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

A page that freezes for seconds during a calculation feels broken to users, and the fix is not more useMemo — it's moving work off the main thread. This pattern gives React developers a clean, reusable abstraction for Web Workers that avoids the common pitfalls of duplicate instances and memory leaks.

Summary

Long-running JavaScript blocks the browser's rendering thread, freezing the UI until the computation finishes. Web Workers run scripts on a separate thread, but integrating them into React's render cycle is tricky: creating a Worker inside a component function spawns a new instance on every re-render, leaking memory and breaking state.

The fix is a custom hook that stores the Worker instance in a useRef, which persists across renders without triggering updates. The hook initializes the Worker once on mount, listens for result messages, and terminates it on unmount. A useState slice handles the loading, result, and error states that actually need to drive the UI.

A full TypeScript example walks through a Fibonacci calculator that would normally lock the page. The pattern extends to image processing, large file parsing, on-device AI inference, and regex highlighting across thousands of records — any task where the cost of structured-clone data transfer is outweighed by keeping the main thread free.

Takeaways
Creating a Web Worker directly inside a React component function body spawns a new Worker on every re-render, causing memory leaks and logic bugs.
Storing the Worker instance in a useRef keeps it alive for the component's full lifetime without triggering re-renders when the reference changes.
A custom hook can encapsulate Worker creation, message listening, error handling, and termination, exposing only a start function and reactive state to the component.
The hook initializes the Worker once inside a useEffect with an empty dependency array and calls terminate() in the cleanup function.
Data passed via postMessage is copied using the structured clone algorithm; for large binary payloads, Transferable Objects enable zero-copy transfer of ownership.
Chrome DevTools' Sources panel includes a Threads view for debugging both the main thread and Worker thread execution stacks side by side.
Conclusions

The pattern treats the Worker as an imperative, long-lived object that sits outside React's declarative model — useRef is the escape hatch that makes this possible without fighting the framework.

Many React performance discussions stop at memoization and virtualization; this approach addresses a harder class of problem where the computation itself, not the rendering, is the bottleneck.

The structured-clone overhead means Workers are not a free lunch — the break-even point depends on whether the computation cost dominates the serialization cost, which matters for real-time or high-frequency tasks.

Concepts & terms
Web Worker
A browser API that runs JavaScript on a background thread, separate from the main UI thread. Workers cannot access the DOM, window, or document objects, and communicate with the main thread via postMessage using structured clone or transferable objects.
useRef
A React Hook that returns a mutable object whose .current property persists across renders. Unlike useState, changing .current does not trigger a re-render, making it suitable for holding non-UI values like timer IDs or Worker instances.
Structured Clone Algorithm
The built-in browser mechanism used by postMessage to copy data between threads. It deep-copies most JavaScript types but cannot clone functions, DOM nodes, or certain objects like Error instances. For large binary data, Transferable Objects can avoid the copy by transferring ownership.
Transferable Objects
Objects like ArrayBuffer and MessagePort that can be transferred between threads with zero-copy semantics. Once transferred, the sending context loses access to the object, making it efficient for large payloads.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗