To use this example use ParcelJs to bundle automatically your worker.js files. if you are using webpack or rollup, you need to install appropriate pluging to bundle worker.js
import { proxy } from 'comlink';
export default async function (store) {
const subscribers = new Set();
let latestState = await store.getState();
store.subscribe(
proxy(async () => {
latestState = await store.getState();
subscribers.forEach((f) => f());
})
);
return {
dispatch: (action) => store.dispatch(action),
getState: () => latestState,
subscribe: (listener) => {
subscribers.add(listener);
return () => subscribers.delete(listener);
},
replaceReducer: () => {
throw new Error('Can’t transfer a function');
},
};
}
const initialState = { count: 0 };
const delayFunction = () => {
console.log('Start to delay...');
const seconds = 3;
const start = new Date().getTime();
const delay = seconds * 1000;
while (true) {
if (new Date().getTime() - start > delay) {
break;
}
}
console.log('Finished delaying');
};
export default (state = initialState, action) => {
switch (action.type) {
case 'increment':
delayFunction();
return { ...state, count: state.count + 1 };
case 'decrement':
delayFunction();
return { ...state, count: state.count - 1 };
default:
return state;
}
};
import { expose } from 'comlink';
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
expose(store);
import React from 'react';
import ReactDOM from 'react-dom';
import { wrap } from 'comlink';
import { Provider, useDispatch, useSelector } from 'react-redux';
import remoteStoreWrapper from './remoStoreWrapper';
import { createStore } from 'redux';
import reducer from './reducer';
const Counter = () => {
const dispatch = useDispatch();
const count = useSelector((state) => state.count);
return (
<div>
Current count: {count}
<button type="button" onClick={() => dispatch({ type: 'increment' })}>
+1
</button>
<button type="button" onClick={() => dispatch({ type: 'decrement' })}>
-1
</button>
</div>
);
};
const App = ({ store }) => {
return (
<Provider store={store}>
<Counter />
<Counter />
<Counter />
</Provider>
);
};
const withOutWebWorker = async () => {
const store = createStore(reducer);
ReactDOM.render(<App store={store} />, document.getElementById('app'));
};
const withWebWorker = async () => {
const worker = new Worker('./store.worker.js');
const remoteStore = wrap(worker);
const store = await remoteStoreWrapper(remoteStore);
ReactDOM.render(<App store={store} />, document.getElementById('app2'));
};
withWebWorker();
withOutWebWorker();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
html,
body {
margin: 0;
width: 100%;
height: 100%;
}
body {
display: flex;
flex-direction: column;
align-items: center;
}
button {
margin: 20px;
border: 1px solid white;
padding: 20px;
cursor: pointer;
}
#message {
font-size: 22px;
}
button:hover {
background: black;
color: #fff;
}
.bouncy-ball {
background: red;
border-radius: 50%;
position: fixed;
top: 22%;
left: 0;
width: 80px;
height: 80px;
margin-top: -40px;
-ms-animation: bounce 3s infinite;
-moz-animation: bounce 3s infinite;
-webkit-animation: bounce 3s infinite;
animation: bounce 3s infinite;
}
@-ms-keyframes bounce {
0% {
left: 0;
}
50% {
left: calc(100% - 80px);
}
100% {
left: 0;
}
}
@-moz-keyframes bounce {
0% {
left: 0;
}
50% {
left: calc(100% - 80px);
}
100% {
left: 0;
}
}
@-webkit-keyframes bounce {
0% {
left: 0;
}
50% {
left: calc(100% - 80px);
}
100% {
left: 0;
}
}
@keyframes bounce {
0% {
left: 0;
}
50% {
left: calc(100% - 80px);
}
100% {
left: 0;
}
}
</style>
</head>
<body>
<center><h1>Redux off main thread using Comlink</h1></center>
<div style="width: 100%; display: flex; justify-content: space-around">
<div>
<h1>No WebWorker</h1>
<div id="app"></div>
<div class="bouncy-ball"></div>
</div>
<div>
<h1>With WebWorker</h1>
<div id="app2"></div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>