DEV Community

Chun
Chun

Posted on

Use Web Workers in Create React App without ejecting and TypeScript

Web Workers are incredibly useful if you need to do some heavy processing without hanging the UI.

This tutorial is aimed at implementing web workers in a create-react-app project without ejecting and avoiding TypeScript errors. If you want to know how to set up a React project you should check out my previous post

Web Workers

In a normal web app, all the JS is running in the main thread, so this means if you execute a very intensive function you could potentially hang the UI until the function has finished, which is not a good user experience. This can be avoided by using web workers because they run scripts in background threads.

Web Workers execute a script from a static file that is separate from your main JS file. Previously, this meant that you would need to eject from create-react-app in order to modify the webpack config and export a separate file. However, with recent developments you can now use web workers in CRA without ejecting!

comlink-loader

There's a great package called comlink-loader that allows you to call functions from a web worker like a class method, and skip the postMessage and onmessage exchange described in the docs.

Install the package:

npm install -D comlink-loader

or 

yarn add comlink-loader
Enter fullscreen mode Exit fullscreen mode

Create the worker

Create a directory called worker and it will contain three files:

  • custom.d.ts
  • index.ts
  • worker.ts

worker.ts is where you will keep the functions that you want executed in a background thread:

/* ./worker/worker.ts */

export function processData(data: string): string {

  // Process the data without stalling the UI

  return data;
}
Enter fullscreen mode Exit fullscreen mode

custom.d.ts is a TypeScript declaration file to avoid compilation errors.

For type safety, add your functions from worker.ts to the class as a method and the return type should be wrapped in a promise.

/* ./worker/custom.d.ts */

declare module 'comlink-loader!*' {
  class WebpackWorker extends Worker {
    constructor();

    // Add any custom functions to this class.
    // Make note that the return type needs to be wrapped in a promise.
    processData(data: string): Promise<string>;
  }

  export = WebpackWorker;
}
Enter fullscreen mode Exit fullscreen mode

index.ts contains the inline loader so you don't have to eject and modify the webpack config

/* ./worker/index.ts */

// eslint-disable-next-line
import Worker from 'comlink-loader!./worker'; // inline loader

export default Worker;
Enter fullscreen mode Exit fullscreen mode

Depending on your linter rules, you may get a build error:

Unexpected '!' in 'comlink-loader!./worker'. Do not use import syntax to configure webpack loader
Enter fullscreen mode Exit fullscreen mode

This is not a problem and the rule can be disabled for the line (see lines 3 and 4 above).

Import into React app

Now the fun part, simply import the worker into your React app and create a new instance to start using it:

/* App.tsx */

import Worker from './worker'

// Create new instance
const instance = new Worker();

const onClick = () => {
  const data = 'Some data';

  return new Promise(async resolve => {

    // Use a web worker to process the data
    const processed = await instance.processData(data);

    resolve(processed);
  });
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

It is quite simple to implement a web worker into your React app, and it will be a major improvement to the user experience if your app does a lot of heavy processing.

If you have any suggestions or questions feel free to leave a comment.

Top comments (7)

Collapse
 
muratas profile image
murat

Hi, thanks for this effort.
Can you start an expressjs server in a web worker in this infrastructure?
A demo would be of great appreciation :-)

Just send a PR :-)
github.com/cchanxzy/tutorial-creat...

Collapse
 
muratas profile image
murat

Hi, thanks for this effort.
Can you start an expressjs server in a web worker in this infrastructure?
A demo would be of great appreciation :-)

Collapse
 
muratas profile image
murat

Hi, thanks for this effort.
Can you start an expressjs server in a web worker in this infrastructure?
A demo would be of great appreciation :-)

Just send a PR :-)

Collapse
 
awesomeironman profile image
Cyrus Gracias

Flawless πŸŽ‰
But I wish there were a way to proxy DOM nodes to perform calculations

Collapse
 
b3kay profile image
B3kay

This is great, do you have an example repo for this? :D

Collapse
 
cchanxzy profile image
Chun

Hi, sorry I only saw this comment yesterday.

Here’s a repo I created as a demo:
github.com/cchanxzy/tutorial-creat...

Hope you and others find it useful.

Collapse
 
vishnuborn1996 profile image
Vishnu Bhaskaran

seems to fail when running in the production build ! did you encounter such an issue?

Error : Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'apply')