Like useReducer, but runs in a worker.

Overview

useWorkerizedReducer

useWorkerizedReducer is like useReducer, but the reducer runs in a worker. This makes it possible to place long-running computations in the reducer without affecting the responsiveness of the app.

Example

// 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();
    }
  }
);

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

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

render(<App />, document.querySelector("main"));

Browser Support

useWorkerizedReducer works in all browsers. Firefox requires a polyfill.

(Currently, useWorkerizedReducer relies on WritableStream, which is available everywhere except Firefox. If you want to support Firefox, I recommend the web-streams-polyfill.)

Details

useWorkerizedReducer takes care of bringing the functionality of useReducer to a worker. It bridges the gap between worker and main thread by duplicating the reducer’s state to the main thread. The reducer manipulates the state object in the worker, and through ImmerJS only patches will be postMessage()’d to keep the main thread’s copy of the state up-to-date.

Due to the communication with a worker, useWorkerizedReducer is inherently asynchronous. In fact, part of the motivation was to enable long-running reducers, which means considerable time can pass between a dispatch() call and the subsequent state change. useWorkerizedReducer will fully finish processing an action before starting the next one, even if the reducer is async.

If a reducer is still running, the busy variable returned by useWorkerizedReducer will be set to true.

API

Exported methods

useWorkerizedReducer(worker, name, initialState): [State, DispatchFunc, isBusy];

isBusy will be true until the initialState has been successfully replicated to the worker. Afterwards, isBusy is true when there actions still being processed, false otherwise.

initWorkerizedReducer(name, reducerFunc, localState?);

name is the name of the reducer, which has to be identical to the name passed into useWorkerizedReducer. reducerFunc is a function of type (state, action, localState) => void | Promise<void>. It behaves the same as the reducer function you pass to the vanilla useReducer hook. In contrast to the reducer functions from the vanilla useReducer hook, it is important to manipulate the state object directly. ImmerJS is recording the operations performed on the object to generate a patch set. Creating copies of the object will not yield the desired effect. Since the modifications to state have to be transferred back to the main thread, the state object can only hold structured cloneable values.

localState is optional, and is a function of type (initialState) => LocalState. It will be called when a new reducer is being created and is expected to return a new local state instance. Local state will not be transferred to the main thread and therefore can hold references to values that are not structured cloneable, like functions or errors.

Convenience exports

For React:

import { ... } from "use-workerized-reducer/react";

For Preact:

import { ... } from "use-workerized-reducer/preact";

If, for some reason, you don’t want to use either of those, you can use the generic export. Note that useWorkerizedReducer takes 3 extra parameters, which have to be the useState, useEffect and useMemo hook in that order.

import { ... } from "use-workerized-reducer";

Apache-2.0

You might also like...

The new BASIC computer that runs in your browser!

The new BASIC computer that runs in your browser!

atto The new BASIC computer that runs in your browser! Try it live: jamesl.me/atto What is atto? atto is a virtual fantasy computer system that's desi

Dec 29, 2022

Runs prettier on a repository's code

prettier-docker-ga Run prettier on your code from Github workflows. Sample Github workflow (this assumes there is a prettier.config.js file in your re

Oct 18, 2022

Runs various integration tests for the composable picasso parachain.

Picasso Integration Tester Picasso Integration Tester is a collection of different implementation tests for the Picasso Polkadot Parachain. Installati

Jan 11, 2022

A URL shortener that runs on Cloudflare Workers

ITP Works A URL shortener that runs on Cloudflare Workers. It stores the rules in Cloudflare KV storage and sends a 301 redirect when a matched pathna

Mar 4, 2022

A Jest runner that runs tests directly in bare Node.js, without virtualizing the environment.

jest-light-runner A Jest runner that runs tests directly in bare Node.js, without virtualizing the environment. Comparison with the default Jest runne

Dec 12, 2022

View maps, graphs, and tables of your save and compete in a casual, evergreen leaderboard of EU4 achievement speed runs. Upload and share your save with the world.

View maps, graphs, and tables of your save and compete in a casual, evergreen leaderboard of EU4 achievement speed runs. Upload and share your save with the world.

PDX Tools PDX Tools is a modern EU4 save file analyzer that allow users to view maps, graphs, and data tables of their save all within the browser. If

Dec 27, 2022

Show a helpful summary of test results in GitHub Actions CI/CD workflow runs

Test Summary Produce an easy-to-read summary of your project's test data as part of your GitHub Actions CI/CD workflow. This helps you understand at-a

Jan 2, 2023

Server-side rendering blog runs on Cloudflare workers

Serverside rendered blog I have tried something completely against to current web trends. What are these? I am using the React as backend framework wi

Jun 24, 2022

This document introduces an early implementation of the Node-RED runtime that runs on resource-constrained microcontrollers (MCUs).

Node-RED MCU Edition Copyright 2022, Moddable Tech, Inc. All rights reserved. Peter Hoddie Updated June 25, 2022 Introduction This document introduces

Jan 3, 2023
Comments
  • Aren't these devDependencies ?

    Aren't these devDependencies ?

    Aren't the following devDependencies?

    • immer
    • npm-run-all
    • vite

    https://github.com/surma/use-workerized-reducer/blob/ee39e9b65a3ad60b94068e2ef447eb565e624982/package.json#L22-L26

    Moving them would stop users of the npm package having to install them?

    opened by andykenward 4
Owner
Surma
Web Platform Advocate. Craving simplicity, finding it nowhere. Internetrovert 🏳️‍🌈
Surma
A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebWorker like neither of those.

Amuchina A work-in-progress HTML sanitizer that strives for: performance like window.Sanitizer, readiness like DOMPurify, and ability to run in a WebW

Fabio Spampinato 9 Sep 17, 2022
Like JSX, but native and fast

esx High throughput React Server Side Rendering For a simplified example of esx in action, check out esx-demo. esx is designed to be a high speed SSR

ESX 645 Jan 2, 2023
Like Obsidian Publish but for self-hosting. Plugin integrations for dataview, admonition, and more.

Obsidian Export Obsidian Publish is great but lacks support for many of the plugins we Obsidian addicts have grown accustomed to — in particular Datav

null 12 Nov 28, 2022
Enables creating databases based on files in Obsidian - like Dataview, but with editing!

Obsidian Database Plugin Do you like Dataview plugin for Obsidian? This one is taking Dataview to next level, but not only allowing you to view the da

Łukasz Tomaszkiewicz 115 Jan 4, 2023
📦 Writing Express but feel like Spring Boot

Springpress Custom top-level framework of Express.js, especially on TypeScript. Springpress provides expressjs utilities out of the box, lets you deep

Vectier 8 Oct 14, 2022
Javascript library for switching fixed elements on scroll through sections. Like Midnight.js, but without jQuery

Library for Switching Fixed Elements on Scroll Sometimes designers create complex logic and fix parts of the interface. Also they colour page sections

Vladimir Lysov 38 Sep 19, 2022
Simba is a city like Florence, Vienna, or San Francisco but built via the internet.

Simba City Project setup Duplicate env.example and .env.test.example and rename to .env and .env.test Firebase Authentication We are going to use fire

Simba City 48 Dec 15, 2022
Customizable masonry Flatlist. it just behave like Flatlist but using ScrollView behind the scene

Would you like to support me? react-native-masonry-grid Customizable masonry Flatlist. it just behave like Flatlist but using ScrollView behind the sc

Numan 5 Sep 7, 2022
🎮 The only Front-End Performance Checklist that runs faster than the others

Front-End Performance Checklist ?? The only Front-End Performance Checklist that runs faster than the others. One simple rule: "Design and code with p

David Dias 15.5k Jan 1, 2023