Introduction to Web Workers in React: A Beginner's Guide

Introduction to Web Workers in React: A Beginner's Guide

·

6 min read

Play this article

Introduction

Web Workers are a way to run JavaScript in the background, separate from the main execution thread. This can be useful for offloading tasks that might block the main thread, such as loading large data sets or performing expensive calculations.

Few examples of how Web Workers might be used:

  1. Loading large data sets: Web Workers can be used to load large data sets asynchronously, without blocking the main thread. This can improve the performance of an application and make it feel more responsive.

  2. Performing expensive calculations: Web Workers can be used to perform expensive calculations, such as training machine learning models or performing complex math, without blocking the main thread.

  3. Offloading image processing tasks: Web Workers can be used to process images, such as resizing or applying filters, without blocking the main thread.

  4. Running simulations or animations: Web Workers can be used to run simulations or animations that might be too resource-intensive to run on the main thread.

There are several types of Web Workers:

Inline Workers

An inline worker is a script that is included directly in the HTML page as a script element. Inline workers are useful for small scripts that do not need to be loaded from an external file.

Here is an example of an inline worker:

<script>
  // Register a callback to process messages from the parent
  self.onmessage = event => {
    // Do some computation with the data from the parent
    const result = event.data * 2;

    // Send the result back to the parent
    self.postMessage(result);
  };
</script>

External Workers

An external worker is a script that is loaded from an external file using the Worker constructor. External workers are useful for larger scripts that need to be separated from the main execution thread.

Here is an example of an external worker:

// worker.js

// Register a callback to process messages from the parent
self.onmessage = event => {
  // Do some computation with the data from the parent
  const result = event.data * 2;

  // Send the result back to the parent
  self.postMessage(result);
};

// App.tsx

import * as React from 'react';

const App: React.FC = () => {
  // Create the Worker object and pass it the URL of the worker script
const workerRef = React.useRef(new Worker('./worker.js'));
 // Get the worker object from the ref
  const worker = workerRef.current;
  // Send some data to the worker
  worker.postMessage(10);

  // Register a callback to process the result from the worker
  worker.onmessage = event => {
    console.log(event.data); // 20
  };

  return <div>Hello World!</div>;
};

export default App;

Shared Workers

A shared worker is a worker that can be shared by multiple pages and can communicate with all of them. Shared workers are useful for cases where multiple pages need to share data or communicate with each other.

Here is an example of a shared worker:

// worker.js

// Register a callback to process messages from the parent
self.onmessage = event => {
  // Do some computation with the data from the parent
  const result = event.data * 2;

  // Send the result back to the parent
  self.postMessage(result);
};


import * as React from 'react';

const App: React.FC = () => {
  // Create the SharedWorker object and pass it the URL of the worker script
const workerRef= React.useRef(new SharedWorker('./worker.js'));
 // Get the worker object from the ref
  const worker = workerRef.current;
  // Send some data to the worker
  worker.port.postMessage(10);

  // Register a callback to process the result from the worker
  worker.port.onmessage = event => {
    console.log(event.data); // 20
  };

  return <div>Hello World!</div>;
};

export default App;

App Demo

We will demonstrate downloading an Excel file using an API and a Web Worker:

  1. Node.js API to retrieve data for the Excel file
import Express from "express"
import dotenv from "dotenv"
import cors from "cors"
dotenv.config()
const PORT = process.env.PORT


const app : Express.Application = Express()

app.use(cors({
    origin: 'http://localhost:5173'
  }));

const data = [
    { name: 'John', age: 30 },
    { name: 'Jane', age: 25 }
  ];

    app.get('/api/data', (req, res) => {
    res.json(data);
    });


app.listen(PORT, () => {
    console.log(`Server is listening on ${PORT}`)
})

This API has a single endpoint, /api/data, which returns a JSON array of data. You can modify this endpoint to retrieve the data from a database or other source as needed.

  1. We will now use this API in the Web Worker, using the Axios API to process the returned data as needed.
import * as React from 'react';
import axios from 'axios';
import { useEffect, useState } from 'react';

const App: React.FC = () => {
  const [url, setUrl] = useState('');

  useEffect(() => {
    // Make an API call to retrieve the data
    axios.get('http://localhost:8080/api/data')
      .then(response => {
        const data = response.data;

        // Create the Worker object and pass it the URL of the worker script
        const worker = new Worker('src/worker.js');

        // Send the data to the worker
        worker.postMessage(data);

        // Register a callback to process messages from the worker
        worker.onmessage = event => {
          // Create a Blob object from the Excel data returned by the worker
          const blob = new Blob([event.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

          // Create a URL for the Blob object
          const objectUrl = URL.createObjectURL(blob);
          setUrl(objectUrl);
        };
      })
      .catch(error => {
        console.error(error);
      });
  }, []);

  return (
    <div>
      <a href={url} download="data.xlsx">Download Excel</a>
    </div>
  );
};

export default App;

//worker.js

importScripts('../node_modules/xlsx/dist/xlsx.full.min.js'); /* imports xlsx  */

// Register a callback to process messages from the parent
self.onmessage = event => {
  // Retrieve the data from the message
  const data = event.data;
  // Create an Excel file from the data using SheetJS or XLSX-populate
  const workbook = self.XLSX.utils.book_new();
  const sheet = self.XLSX.utils.json_to_sheet(data);
  self.XLSX.utils.book_append_sheet(workbook, sheet);
  const excelData = self.XLSX.write(workbook, { type: 'array', bookType: 'xlsx' });

  // Send the Excel data back to the parent
  self.postMessage(excelData);
};

This code uses the useEffect hook to make an API call to retrieve the data and create the Web Worker when the component is mounted. It then sends the data to the worker using the postMessage method and registers a callback to process the Excel data returned by the worker using the onmessage method. Finally, it creates a Blob object and a URL for the Blob and renders a link element that allows the user to download the Excel file.

To use the XLSX library in your web worker, you will need to import it in your web worker script. You can do this using the importScripts() function, which allows you to load and execute external scripts in your web worker.

Conclusion

Web workers are a useful tool for offloading complex or time-consuming tasks to a separate thread, allowing the main thread to remain responsive and improve the overall performance of your application. In this article, we learned how to use Web Workers in a React application, including creating and communicating with a worker and using it to perform a long-running task. We also saw how to use the SheetJS library to create an Excel file from data received from an API call, and how to use the XLSX-populate library to populate an Excel file with data. By following the steps in this guide, you can easily incorporate Web Workers into your React application to improve its performance and user experience.

Happy coding! ✨

If you have any questions, You can reach out to me on:

Twitter

LinkedIn

Did you find this article valuable?

Support Jay Desai by becoming a sponsor. Any amount is appreciated!