Best Practices for using Web Workers with React Hooks and Lifecycle Events

Andrew Westwick
3 min readJul 7, 2020

Recently I ran into some issues with the way our team was managing web workers in our React project. We use the workers to offload some heavy searching using the fuse.js library with large datasets. I was surprised to find that there wasn’t a lot written about this already (although maybe my google-fu was lacking).

Our original setup looked something like this:

By doing it this way, the worker is instantiated at a higher level scope than the component itself, which causes some problems. First, the worker is always created at runtime even before the component is ever mounted to the DOM, (even if the component is NEVER mounted to the DOM), so it just sits there uselessly taking up memory. Second, if you tried to create two instances of SomeComponent, the second instance will overwrite the searchWorker variable and make it inaccessible in the first instance of the component.

My first attempt to fix this and move it into the lifecycle of the component was by utilizing useState like so:

Of course, those who are more familiar with React than myself will notice a problem with this instantly: the SearchWorker is being instantiated every time the component re-renders, which can cause dozens of instances to fill up the memory depending on how often this happens.

So we need a way to create and destroy the workers in conjunction with the lifecycle of the React component. Let’s move the instantiation of the worker inside the useEffect hook for component initialization:

We’re getting closer, but we still have a couple problems. We need to be mindful of the react lifecycle and remember that onmessageandpostMessagewill not work at this point as the seachWorker variable we’re setting in state won’t become available until the next cycle. We also want to explicitly terminate the worker instance when the component unmounts. We can fix both of those problems by using a temporary searchWorkerInstance variable scoped only to the useEffect function:

This works, but there are still a couple things to note to that I had to change for my specific use case. If the postMessage call references some other component props or needs to also be called in other methods of the component, then we need to move the postMessage call into it’s own hook. Again this will vary slightly depending on your specific use case, but my final code ended up looking something like this:

Pulling out the postMessagecall into it’s own useEffecthook allows us to reference any props we might need to react to and send to the web worker. The reason we must wrap the postMessage in an if is that this hook will run the first time the component mounts, at which point searchWorker will be undefined. Then, after the first useEffect runs and sets the searchWorker, the second useEffect hook will run again and will now be able to post messages to the worker successfully.

You can verify that the workers are being created and destroyed with the lifecycle of your component by watching the Memory tab in Chrome dev tools (Javascript VM instances).

Hope this helps someone with integrating workers into their React project properly. If I missed anything important please let me know!

--

--

Andrew Westwick

Software Engineer at @iheartmedia Previously CTO & co-founder at http://funneldash.com and http://kickfurther.com . Entrepreneur, Full Stack Web Developer