Use Web Workers in Create React App without ejecting and TypeScript
Tags: react, typescript, javascript, webdev
Published: 2020-02-23 21:53:26

[Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_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](https://dev.to/cchanxzy/set-up-a-new-react-project-with-typescript-eslint-and-prettier-222b)

# 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](https://github.com/GoogleChromeLabs/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](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers).

Install the package:

```bash
npm install -D comlink-loader

or 

yarn add comlink-loader
```

# 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:

```typescript
/* ./worker/worker.ts */

export function processData(data: string): string {

  // Process the data without stalling the UI

  return data;
}
```


**`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.

```typescript
/* ./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;
}
```


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

```typescript
/* ./worker/index.ts */

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

export default Worker;
```

Depending on your linter rules, you may get a build error: 
```bash
Unexpected '!' in 'comlink-loader!./worker'. Do not use import syntax to configure webpack loader
```
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:

```typescript
/* 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);
  });
};
```

# 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.