Popoola Temitope I'm a software developer and technical writer. I love learning about new technology and am always ready to share ideas with others.

How to place React useReducer Hooks into web workers

4 min read 1268

Usereducer Web Workers

When a web page loads, it becomes unresponsive until all of the resources load completely. When the resource is loading, users may be unable to perform certain functions on the page, like clicking, selecting, or dragging elements. web workers help us to solve this problem, making our application dynamic so it loads faster.

In this tutorial, we’ll learn how to use web workers in a React application. We’ll also learn how to use the useReducer Hook within a web worker with useWorkerizedReducer. Let’s get started!

Table of contents

What is a web worker?

A web worker is a JavaScript script that runs in the background without interfering with the execution of other scrips.

Because JavaScript is a single-threaded language, it cannot run several scripts at the same time, which is an issue for running large computation scripts. web workers help load heavy computation scripts in the background without affecting the performance of the page.

web worker syntax

const worker = new Worker(new URL("./worker.js", import.meta.url), {
 type: "module",
});

The worker constructor accepts two parameters; the first is the worker file name, and the second is the worker type. The type worker can either be classic or module. If the type is not specified, the default type will be classic.

In this tutorial, we’ll use the module type because the reducer can only be used inside a component. To use the import function inside the web worker component, we must add the import.meta.url to the URL constructor. We’ll demonstrate this later in the tutorial.

What is the useReducer Hook?

useReducer is an additional React Hook that is used to store and update states. It accepts three parameters, reducer, initial state, and as the last parameter, initial function, which is optional:

const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducer returns an array containing the current state value, as well as a dispatch function to which you can supply an action to be executed.

The dispatch function accepts an object that specifies the type of action to be executed. It essentially communicates the type of action to the reducer function, which, of course, updates the state.

The reducer function

A reducer is a function that accepts two parameters, the current state and an action object. It uses the action it receives to determine the change in state and return the new state.

We made a custom demo for .
No really. Click here to check it out.

The code below demonstrates how reducer function can be use to change state:

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};

   default:
      throw new Error();
  }
}

Whenever the reducer function is triggered by the action type, it returns the new state as a change inside the reducer function.

An action is a type of object that instructs the reducer on how to change the state. It must have a type property. The action type can be used in a conditional statement to make decisions on how the state changes, as shown in the code block above.

To make the reducer run in a web worker, we have to use useWorkerizedReducer.

useWorkerizedReducer

useWorkerizedReducer is similar to useReducer, except it allows the reducer to execute in a worker, allowing us to create a dynamic React application.

useWorkerizedReducer also allows long-running computations to be placed in the reducer without affecting the application’s responsiveness. useWorkerizedReducer is responsible for providing useReducer‘s capabilities to a worker.

By replicating the reducer’s state to the main thread, useWorkerizedReducer bridges the gap between the worker and the main thread. The reducer manipulates the worker’s state object, and the only patch will be postMessage() through Immer to keep the main thread’s copy of the state current.

Building a simple counter application

To learn how to place Reducer in a web worker, let’s create a simple counter program that will return whenever the current state is changed. To create the new program, open the command line and enter the following commands:

npx create-react-app my-app
cd my-app
npm start

After the application has been installed successfully, we need to install useWorkerizedReducer as a dependency on our program to use it. To install useWorkerizedReducer, run the following command in the terminal:

npm i use-workerized-reducer

Now that we’ve successfully installed useWorkerizedReducer, let’s go ahead and create a worker.js module file.

Creating worker.js

Since we’re using reducer in the worker.js file, we’ll create the worker.js file inside the src folder.To create the worker.js file, click on create a new file, name the file worker.js, then save it inside the src folder, as shown below:

Workerjs Src Folder

Now that we’ve created the worker.js file, let’s add the reducer code below inside it:

// worker.js
import { initWorkerizedReducer } from "use-workerized-reducer";

initWorkerizedReducer(
 "counter", // Name of the reducer
 async (state, action) => {
   // Reducers can be async!
   // Manipulate `state` directly. ImmerJS will take
   // care of maintaining referential equality.
   switch (action.type) {
     case "increment":
       state.counter += 1;
       break;
     case "decrement":
       state.counter -= 1;
       break;
     default:
       throw new Error();
   }
 }
);

From the code above, we import initWorkerizedReducer from use-workerized-reducer, which is made available from the useWorkerizedReducer dependency.

The initWorkerizedReducer() takes two parameters; the first is the reducer name(counter), and async function is the second.

Adding the web worker

Now that we have the worker.js file ready, we need to import useWorkerizedReducer from use-workerized-reducer/react, which gives us access to call the reducer function from the worker file:

// main.js
import { render, h, Fragment } from "react";
import { useWorkerizedReducer } from "use-workerized-reducer/react";

// Spin up the worker running the reducers.
const worker = new Worker(new URL("./worker.js", import.meta.url), {
 type: "module",
});

function App() {
 // A worker can contain multiple reducers, each with a unique name.
 // `busy` is true if any action is still being processed.
 const [state, dispatch, busy] = useWorkerizedReducer(
   worker,
   "counter", // Reducer name
   { counter: 0 } // Initial state
 );

 return (
   <>
     Count: {state.counter}
     <button disabled={busy} onClick={() => dispatch({ type: "decrement" })}>
       -
     </button>
     <button disabled={busy} onClick={() => dispatch({ type: "increment" })}>
       +
     </button>
   </>
 );
}

export default App;

The useWorkerizedReducer function takes three parameters and returns a single value. The current state is the first parameter, the action is the second, and initial state is the third parameter. The data we’re working with is the state. A dispatch function executes an action that is passed to the reducer function.

Busy will remain true until the worker’s initial state counter has been successfully replicated to the worker. After that, Busy returns true if actions are still being processed and false otherwise.

The reducer changes the state based on the action type. The action types increment, decrement, and reset are all action types that update the app’s state when dispatched.

The initial state is { counter: 0 }. When the increment action type is dispatched, we simply set count {state.count + 1} to increment the state.

Running the program

To start the application, enter the command below in the command line:

npm start

Conclusion

In this article, we covered a quick overview of web workers and useReducer as an additional Hook in React, as well as how to construct and add web worker files to your React applications.

We also discussed useWorkerizedReducer, which brings the functionality of useReducer to a web worker. Finally, we covered using reducer inside the web worker with the help of useWorkerizedReducer.

I hope you enjoyed this article! Be sure to leave a comment if you have any questions. Happy coding!

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are hard to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, and tracking slow network requests and component load time, try LogRocket.

LogRocket is like a DVR for web and mobile apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app's performance, reporting with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps — .

Popoola Temitope I'm a software developer and technical writer. I love learning about new technology and am always ready to share ideas with others.

Leave a Reply