useReducer
Hooks into web workersWhen 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!
useReducer
Hook?reducer
functionuseWorkerizedReducer
worker.js
A web worker is a JavaScript script that runs in the background without interfering with the execution of other scripts.
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.
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.
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.
reducer
functionA 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.
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.
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.
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:
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.
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.
To start the application, enter the command below in the command line:
npm start
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!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowCompare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.