DEV Community

loading...

Web-workers in Create React App (CRA) without unmounting

nicolasrannou profile image Nicolas Rannou Updated on ・3 min read

There are a lot of great options available when it comes to using web-workers in your web-app. Unfortunately, it can be challenging to use in a Create React Application (CRA) without un-mounting your application.

In this article, we share my journey of using web-workers in CRA (without un-mounting!)

Table of contents generated with markdown-toc

Why not unmounting?

  • Webpack configuration can be overwhelming
  • It can be difficult to upgrade to the next version of react-scripts
  • CRA team will not help us if something goes side-ways

Finding the right tools for YOU

The right tool for me would check the items in the following list:

  • Must: Not unmounting CRA
  • Must: Support transferable objects
  • Nice to have: Easy to use
  • Nice to have: Typescript support
  • Nice to have: Pooling system

Attempt 1: Threads.js

Threads.js looked very promising and feature complete (according to my requirements).

However, to use it within webpack, you must use its companion loader (threads-plugin).

That means you must access the webpack configuration file and add the loader there. That was a no-go for me because it implies un-mounting CRA.

Attempt 2: Workarize

The next step was to find a tool that leverages webpack loaders rather than plugins. With webpack loaders, we can inline the loader in the import statement, therefore, we do not need to modify the webpack configuration file to use it.

\\ inline the loader in the import statement
import {Something} from 'my-loader!./utils/filename'

workarize and its workarize-loader seemed pretty cool. Although it doesn't support pooling the API looked awesome and super intuitive.

But it doesn't support transferable objects at the time I write this post (03-26-2020), so that was another no-go.

Attempt 3: Worker-loader

Next, I tested worker-loader and its transferable objects.

It worked fine but using web-workers without sugar on top just didn't feel right for me - we can do better!

Attempt 4: Comlink

Then comes comlink. It "makes WebWorkers enjoyable again" (to quote their README).

comlink works nicely with the worker-loader and adds this nice touch on top of the web-workers to make it easy to use.

It may provide features you do not need but works fine in my experience (so far 🤞).

It also works well with Typescript.

It doesn't support pooling but I can live with that!

How it looks

\\ main.ts
import * as Comlink from 'comlink';
/* eslint-disable import/no-webpack-loader-syntax */
import Worker from 'worker-loader!../util/worker';

async function init() {
  const worker = new Worker();
  const obj = Comlink.wrap(worker);
  await obj.inc();
}
init();

\\ worker.ts
import * as Comlink from 'comlink';

const obj = {
  counter: 0,
  inc() {
    this.counter++;
  }
};

Comlink.expose(obj);

That's it

Each tool that was tested is great and you should really consider what you are looking for before making an informed decision.

I can live without pooling as long as it is enjoyable to work with the web-worker so that's why I chose comlink.

TLDR:

  • ✅Must: Not unmounting CRA
  • ✅Must: Support transferable objects
  • ✅Nice to have: Easy to use
  • ✅Nice to have: Typescript support
  • 🔴Nice to have: Pooling system

Thanks for reading!

Discussion (7)

Collapse
yonixw profile image
Yehonatan Water Man

I had to use the defenition of "typings/custom.d.ts" from stackoverflow.com/questions/613825...

// typings/custom.d.ts
declare module "worker-loader!*" {
  class WebpackWorker extends Worker {
    constructor();
  }

  export default WebpackWorker;
}
Enter fullscreen mode Exit fullscreen mode
Collapse
lolero profile image
Dr. Rodrigo Segura

Thanks for a very useful and informative blog post!

It works nicely when I use one WebWorker! However, if I try to use more than one WebWorker in my codebase, I get two errors at compile time:

Conflict: Multiple assets emit different content to the same filename static/js/bundle.worker.js

Conflict: Multiple assets emit different content to the same filename static/js/bundle.worker.js.map

Did you experience this as well and if so, were you able to resolve the problem?

Collapse
larrywal profile image
larrywal • Edited

Sorry - I'm a bit confused - am trying to use comlink with my create-react-app and not ejecting. But I think you're missing steps? I installed both comlink and worker-loader, but just using the code above in option 4 doesn't work. I modified my worker-loader import to point to my worker.js, but that is showing issues that it can't find needed modules (e.g. webpack/lib/node/NodeTargetPlugin". Can you please elaborate the specific setup steps?

Collapse
xinaesthete profile image
Peter Todd

Another option is to use someting like Rescripts to customise WebPack configuration without ejecting CRA.

Collapse
clivend profile image
clivend

what do you mean by "It doesn't support pooling"? thanks!

Collapse
codingdive profile image
Mikey Stengel

Thank you for the great blog post!

FYI: The link in the TLDR for transferable objects is broken.

Collapse
porkopek profile image
Porkopek

I think you meant don't eject the app, not unmount. Thanks for list all these projects. I didn't know about comlink. Have to try

Forem Open with the Forem app