Redux Bundler Web Worker Utilities
redux-bundler-worker provides utilities for offloading an entire redux-bundler application into a Web Worker, allowing the main thread to focus solely on rendering. It achieves this by offering a `getStoreProxy` function for the main thread, which mimics the redux-bundler store API, and a `setUpWorker` function for the worker thread to initialize the actual redux-bundler store. This approach aims to demonstrate a feature-complete mechanism for isolating Redux logic. The current stable version, `0.0.3`, is explicitly stated as being "largely for demonstration purposes" and not shipped in any production applications by its author. As such, it does not have a clear release cadence and should be considered experimental. Its key differentiator is the complete isolation of Redux state management and logic within a worker, reducing main thread overhead.
Common errors
-
ReferenceError: Worker is not defined
cause Attempting to instantiate a `Worker` object in a Node.js environment or a non-browser context.fixWeb Workers are a browser-specific API. Ensure your code runs in a browser environment. If testing, use a browser-like testing environment (e.g., JSDOM with Web Worker polyfills if absolutely necessary, but generally avoided for worker-specific logic). -
Uncaught TypeError: storeProxy.select... is not a function
cause The `storeProxy` has not yet fully initialized or established communication with the worker, or the `redux-bundler` store inside the worker hasn't finished composing its bundles and exposing selectors.fixEnsure that the Web Worker is fully loaded and `setUpWorker` has been called successfully within it. Add logging to both main and worker threads to track initialization status. Also, verify that the selector name (`select...`) actually exists in your bundles. -
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html".
cause The browser failed to load the worker script (`/build/worker.js`) because the server returned an HTML page (e.g., a 404 error page) instead of a JavaScript module, or the server is not configured to serve `.js` files with the correct `application/javascript` MIME type for module workers.fixVerify the path to your `worker.js` file is correct and accessible at runtime. Check your web server or bundler configuration to ensure `worker.js` is served with the correct MIME type (e.g., `application/javascript` or `text/javascript`) and that the file exists at the specified URL. For ES Modules in workers, ensure `new Worker('/path/to/worker.js', { type: 'module' })` is used. -
Error: Worker is not a constructor
cause Trying to use `new Worker()` in an environment where the global `Worker` object is not available or is not a constructible function, possibly due to strict Content Security Policy (CSP) or an unusual execution environment.fixCheck your Content Security Policy (CSP) for `worker-src` or `script-src` directives that might be blocking Web Workers. Ensure you are running in a standard browser environment. Some older browser versions or highly restricted environments might not fully support Web Workers or ESM workers.
Warnings
- breaking The author explicitly states this library is 'largely for demonstration purposes' and has not been used in production. It is not recommended for production environments due to potential instability, lack of active maintenance, and its experimental nature.
- gotcha Debugging state and actions within a Web Worker can be significantly more complex than in the main thread. Standard Redux DevTools might not work out-of-the-box without additional custom integration for message passing.
- gotcha Interacting with the DOM, browser APIs (like `localStorage`, `indexedDB` directly, or `geolocation`), or global event listeners from within the Web Worker is not possible. Operations requiring main thread access must be explicitly marshalled back.
- gotcha The overhead of serializing and deserializing state and actions for message passing between the main thread and the worker can impact performance, especially with large or frequently updated state objects.
- gotcha Web Worker module resolution (importing other modules within the worker script) can be tricky and requires proper bundler configuration (e.g., Webpack's worker-loader or modern bundlers supporting ESM workers) to ensure all dependencies are correctly included and paths are resolved.
Install
-
npm install redux-bundler-worker -
yarn add redux-bundler-worker -
pnpm add redux-bundler-worker
Imports
- getStoreProxy
const getStoreProxy = require('redux-bundler-worker').getStoreProxyimport { getStoreProxy } from 'redux-bundler-worker' - setUpWorker
const setUpWorker = require('redux-bundler-worker').setUpWorkerimport { setUpWorker } from 'redux-bundler-worker'
Quickstart
/* --- main.js (Main Thread) --- */
import { render, h } from 'preact';
import { Provider } from 'redux-bundler-preact';
import { getStoreProxy } from 'redux-bundler-worker';
// A dummy RootComponent for demonstration
const RootComponent = ({ children }) => h('div', null, h('h1', null, 'App Running in Worker'), children);
// Create the Web Worker instance, pointing to your bundled worker script
// Ensure '/build/worker.js' is correctly served by your web server/bundler
const worker = new Worker('/build/worker.js', { type: 'module' });
// Get a store proxy that mimics the redux-bundler store API
// All actions dispatched and selectors called on this proxy will be marshalled to the worker
const storeProxy = getStoreProxy(worker);
// Render your Preact app, passing the store proxy to the Provider
render(
h(Provider, { store: storeProxy }, h(RootComponent)),
document.getElementById('app-root')
);
console.log('Main thread initialized with worker-proxied Redux store.');
/* --- worker.js (Web Worker Thread) --- */
import { composeBundles } from 'redux-bundler';
import { setUpWorker } from 'redux-bundler-worker';
// Define a simple Redux Bundler bundle inside the worker
const appBundle = {
name: 'app',
reducer: (state = { count: 0 }, action) => {
if (action.type === 'INCREMENT') {
return { ...state, count: state.count + 1 };
}
return state;
},
doIncrement: () => ({ dispatch }) => {
dispatch({ type: 'INCREMENT' });
},
selectCount: (state) => state.app.count,
};
// Compose the bundles to create the main Redux Bundler store function
const getWorkerStore = (initialData = {}) => composeBundles(appBundle)(initialData);
// Set up the worker, passing the function that returns the composed store
setUpWorker(getWorkerStore);
console.log('Web Worker initialized with Redux Bundler store.');
// Example of interacting from the main thread after setup (e.g., in browser console)
// window.store = storeProxy; // expose for debugging
// storeProxy.doIncrement();
// storeProxy.selectCount();