0
0
ReactHow-ToBeginner · 4 min read

How to Use Web Workers in React for Background Tasks

In React, you can use Web Workers to run heavy tasks in the background by creating a worker script and communicating with it via postMessage and onmessage. Use the useEffect hook to start and clean up the worker, ensuring your UI stays responsive.
📐

Syntax

To use a web worker in React, you create a new Worker instance with the path to your worker script. Then, you send messages to the worker using postMessage and listen for responses with onmessage. Clean up the worker when the component unmounts.

  • new Worker('worker.js'): creates the worker
  • worker.postMessage(data): sends data to the worker
  • worker.onmessage = (event) => {}: receives data from the worker
  • worker.terminate(): stops the worker
javascript
const worker = new Worker('worker.js');

worker.postMessage('start');

worker.onmessage = (event) => {
  console.log('Message from worker:', event.data);
};

// When done
worker.terminate();
💻

Example

This example shows a React component that uses a web worker to calculate Fibonacci numbers without freezing the UI. The worker runs the calculation and sends the result back.

javascript
import React, { useState, useEffect, useRef } from 'react';

// worker.js content as a Blob
const workerCode = () => {
  self.onmessage = (e) => {
    const fib = (n) => {
      if (n <= 1) return n;
      return fib(n - 1) + fib(n - 2);
    };
    const result = fib(e.data);
    self.postMessage(result);
  };
};

const codeAsString = workerCode.toString();
const blob = new Blob(['(' + codeAsString + ')()'], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(blob);

export default function FibonacciWorker() {
  const [input, setInput] = useState(10);
  const [result, setResult] = useState(null);
  const workerRef = useRef(null);

  useEffect(() => {
    workerRef.current = new Worker(workerUrl);
    workerRef.current.onmessage = (e) => {
      setResult(e.data);
    };
    return () => {
      workerRef.current.terminate();
      URL.revokeObjectURL(workerUrl);
    };
  }, []);

  useEffect(() => {
    if (workerRef.current) {
      setResult('Calculating...');
      workerRef.current.postMessage(input);
    }
  }, [input]);

  return (
    <main>
      <label htmlFor="fibInput">Enter a number:</label>
      <input
        id="fibInput"
        type="number"
        min="0"
        max="40"
        value={input}
        onChange={(e) => setInput(Number(e.target.value))}
        aria-label="Fibonacci input number"
      />
      <p>Fibonacci result: <strong>{result}</strong></p>
    </main>
  );
}
Output
User enters a number, e.g., 10, and the page shows: Fibonacci result: 55
⚠️

Common Pitfalls

Common mistakes when using web workers in React include:

  • Not terminating the worker on component unmount, causing memory leaks.
  • Trying to import React or DOM APIs inside the worker script (workers run in a separate thread without access to DOM).
  • Passing complex objects that are not serializable via postMessage.
  • Not handling worker errors or messages properly.

Always clean up workers and keep worker code simple and self-contained.

javascript
/* Wrong: Not terminating worker */
useEffect(() => {
  const worker = new Worker('worker.js');
  worker.postMessage('start');
  worker.onmessage = (e) => console.log(e.data);
  // Missing worker.terminate() on cleanup
}, []);

/* Right: Terminate worker on cleanup */
useEffect(() => {
  const worker = new Worker('worker.js');
  worker.postMessage('start');
  worker.onmessage = (e) => console.log(e.data);
  return () => {
    worker.terminate();
  };
}, []);
📊

Quick Reference

Tips for using web workers in React:

  • Use useEffect to create and clean up workers.
  • Communicate with workers using postMessage and onmessage.
  • Keep worker code free of React or DOM dependencies.
  • Use Blob URLs for inline workers or separate files for bigger projects.
  • Terminate workers to avoid memory leaks.

Key Takeaways

Create and terminate web workers inside React's useEffect to manage lifecycle.
Use postMessage and onmessage to send and receive data between React and the worker.
Keep worker scripts simple and free from React or DOM code.
Use Blob URLs for inline workers to avoid extra files.
Always terminate workers to prevent memory leaks.