├── .gitignore
├── src
├── index.ts
├── expose.ts
└── wrap.ts
├── examples
├── 01_minimal
│ ├── src
│ │ ├── slow_fib.worker.js
│ │ └── index.js
│ ├── public
│ │ └── index.html
│ └── package.json
└── 02_typescript
│ ├── src
│ ├── slow_fib.worker.js
│ ├── index.ts
│ ├── App.tsx
│ ├── Main.tsx
│ └── DisplayFib.tsx
│ ├── public
│ └── index.html
│ └── package.json
├── CHANGELOG.md
├── __tests__
└── 01_basic_spec.ts
├── tsconfig.json
├── .github
└── workflows
│ ├── ci.yml
│ └── cd.yml
├── webpack.config.js
├── LICENSE
├── .eslintrc.json
├── package.json
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 | *.swp
3 | node_modules
4 | /dist
5 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export { expose } from './expose';
2 | export { wrap } from './wrap';
3 |
--------------------------------------------------------------------------------
/examples/01_minimal/src/slow_fib.worker.js:
--------------------------------------------------------------------------------
1 | import { expose } from 'react-suspense-worker';
2 |
3 | const fib = (i) => (i <= 1 ? i : fib(i - 1) + fib(i - 2));
4 |
5 | expose(fib);
6 |
--------------------------------------------------------------------------------
/examples/02_typescript/src/slow_fib.worker.js:
--------------------------------------------------------------------------------
1 | import { expose } from 'react-suspense-worker';
2 |
3 | const fib = (i) => (i <= 1 ? i : fib(i - 1) + fib(i - 2));
4 |
5 | expose(fib);
6 |
--------------------------------------------------------------------------------
/examples/01_minimal/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | react-suspense-worker example
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/02_typescript/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | react-suspense-worker example
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## [Unreleased]
4 |
5 | ## [0.1.0] - 2022-06-09
6 | ### Changed
7 | - Update with React 18
8 |
9 | ## [0.0.1] - 2020-01-19
10 | ### Added
11 | - Initial release
12 |
--------------------------------------------------------------------------------
/__tests__/01_basic_spec.ts:
--------------------------------------------------------------------------------
1 | import {
2 | expose,
3 | wrap,
4 | } from '../src/index';
5 |
6 | describe('basic spec', () => {
7 | it('exported function', () => {
8 | expect(expose).toBeDefined();
9 | expect(wrap).toBeDefined();
10 | });
11 | });
12 |
--------------------------------------------------------------------------------
/examples/02_typescript/src/index.ts:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import App from './App';
5 |
6 | const ele = document.getElementById('app');
7 | if (!ele) throw new Error('no app');
8 | createRoot(ele).render(React.createElement(App));
9 |
--------------------------------------------------------------------------------
/examples/02_typescript/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from 'react';
2 |
3 | import Main from './Main';
4 |
5 | const App: React.FC = () => (
6 | Loading...}>
7 |
8 |
9 |
10 |
11 | );
12 |
13 | export default App;
14 |
--------------------------------------------------------------------------------
/src/expose.ts:
--------------------------------------------------------------------------------
1 | import { expose as comlinkExpose } from 'comlink';
2 |
3 | /**
4 | * Expose a value in worker thread to be wrapped in main thread
5 | *
6 | * @example
7 | * import { expose } from 'react-suspense-worker';
8 | *
9 | * const fib = (i) => (i <= 1 ? i : fib(i - 1) + fib(i - 2));
10 | *
11 | * expose(fib);
12 | */
13 | export const expose = comlinkExpose;
14 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "target": "es5",
5 | "esModuleInterop": true,
6 | "module": "esnext",
7 | "moduleResolution": "node",
8 | "jsx": "react",
9 | "allowJs": true,
10 | "noUnusedLocals": true,
11 | "noUnusedParameters": true,
12 | "noUncheckedIndexedAccess": true,
13 | "exactOptionalPropertyTypes": true,
14 | "sourceMap": true,
15 | "baseUrl": ".",
16 | "paths": {
17 | "react-suspense-worker": ["./src"]
18 | },
19 | "outDir": "./dist"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/01_minimal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-suspense-worker-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "latest",
7 | "react-dom": "latest",
8 | "react-suspense-worker": "latest",
9 | "react-scripts": "latest"
10 | },
11 | "scripts": {
12 | "start": "react-scripts start",
13 | "build": "react-scripts build",
14 | "test": "react-scripts test",
15 | "eject": "react-scripts eject"
16 | },
17 | "browserslist": [
18 | ">0.2%",
19 | "not dead",
20 | "not ie <= 11",
21 | "not op_mini all"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/examples/02_typescript/src/Main.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useTransition } from 'react';
2 |
3 | import DisplayFib from './DisplayFib';
4 |
5 | const Main: React.FC = () => {
6 | const [number, setNumber] = useState(1);
7 | const [isPending, startTransition] = useTransition();
8 | const onClick = () => {
9 | startTransition(() => {
10 | setNumber((c) => c + 1);
11 | });
12 | };
13 | return (
14 |
15 | number: {number}
16 | +1
17 | {isPending && 'Pending...'}
18 |
19 |
20 | );
21 | };
22 |
23 | export default Main;
24 |
--------------------------------------------------------------------------------
/examples/02_typescript/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-suspense-worker-example",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@types/react": "latest",
7 | "@types/react-dom": "latest",
8 | "react": "latest",
9 | "react-dom": "latest",
10 | "react-suspense-worker": "latest",
11 | "react-scripts": "latest",
12 | "typescript": "latest"
13 | },
14 | "scripts": {
15 | "start": "react-scripts start",
16 | "build": "react-scripts build",
17 | "test": "react-scripts test",
18 | "eject": "react-scripts eject"
19 | },
20 | "browserslist": [
21 | ">0.2%",
22 | "not dead",
23 | "not ie <= 11",
24 | "not op_mini all"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/examples/02_typescript/src/DisplayFib.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | import { wrap } from 'react-suspense-worker';
4 |
5 | type Fib = (i: number) => number;
6 |
7 | const fib = wrap(new Worker(new URL('./slow_fib.worker', import.meta.url)));
8 |
9 | const fastFib = (i: number) => {
10 | if (i <= 1) return i;
11 | let a = 0;
12 | let b = 1;
13 | for (let x = 1; x < i; x += 1) {
14 | [a, b] = [b, a + b];
15 | }
16 | return b;
17 | };
18 |
19 | type Props = {
20 | number: number;
21 | };
22 |
23 | const DisplayFib: React.FC = ({ number }) => {
24 | const result = fib(number);
25 | const result2 = fastFib(number);
26 | return (
27 |
28 |
result: {result}
29 |
result2: {result2}
30 |
31 | );
32 | };
33 |
34 | export default DisplayFib;
35 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | pull_request:
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v2
12 |
13 | - name: Setup Node
14 | uses: actions/setup-node@v1
15 | with:
16 | node-version: '12.x'
17 |
18 | - name: Get yarn cache
19 | id: yarn-cache
20 | run: echo "::set-output name=dir::$(yarn cache dir)"
21 |
22 | - name: Cache dependencies
23 | uses: actions/cache@v1
24 | with:
25 | path: ${{ steps.yarn-cache.outputs.dir }}
26 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
27 | restore-keys: |
28 | ${{ runner.os }}-yarn-
29 |
30 | - name: Install dependencies
31 | run: yarn install
32 |
33 | - name: Test
34 | run: yarn test
35 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-var-requires
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 |
4 | const { DIR, EXT = 'ts' } = process.env;
5 |
6 | module.exports = {
7 | mode: 'development',
8 | devtool: 'cheap-module-source-map',
9 | entry: `./examples/${DIR}/src/index.${EXT}`,
10 | output: {
11 | publicPath: '/',
12 | },
13 | plugins: [
14 | new HtmlWebpackPlugin({
15 | template: `./examples/${DIR}/public/index.html`,
16 | }),
17 | ],
18 | module: {
19 | rules: [{
20 | test: /\.[jt]sx?$/,
21 | exclude: /node_modules/,
22 | loader: 'ts-loader',
23 | options: {
24 | transpileOnly: true,
25 | },
26 | }],
27 | },
28 | resolve: {
29 | extensions: ['.js', '.jsx', '.ts', '.tsx'],
30 | alias: {
31 | 'react-suspense-worker': `${__dirname}/src`,
32 | },
33 | },
34 | devServer: {
35 | port: process.env.PORT || '8080',
36 | static: {
37 | directory: `./examples/${DIR}/public`,
38 | },
39 | historyApiFallback: true,
40 | },
41 | };
42 |
--------------------------------------------------------------------------------
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
1 | name: CD
2 |
3 | on:
4 | push:
5 | tags:
6 | - v*
7 |
8 | jobs:
9 | publish:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 |
14 | - name: Setup Node
15 | uses: actions/setup-node@v1
16 | with:
17 | node-version: '12.x'
18 | registry-url: 'https://registry.npmjs.org'
19 |
20 | - name: Get yarn cache
21 | id: yarn-cache
22 | run: echo "::set-output name=dir::$(yarn cache dir)"
23 |
24 | - name: Cache dependencies
25 | uses: actions/cache@v1
26 | with:
27 | path: ${{ steps.yarn-cache.outputs.dir }}
28 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
29 | restore-keys: |
30 | ${{ runner.os }}-yarn-
31 |
32 | - name: Install dependencies
33 | run: yarn install
34 |
35 | - name: Test
36 | run: yarn test
37 |
38 | - name: Compile
39 | run: yarn run compile
40 |
41 | - name: Publish
42 | run: npm publish
43 | env:
44 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2020-2022 Daishi Kato
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "plugins": [
4 | "@typescript-eslint",
5 | "react-hooks"
6 | ],
7 | "extends": [
8 | "plugin:@typescript-eslint/recommended",
9 | "airbnb"
10 | ],
11 | "env": {
12 | "browser": true
13 | },
14 | "settings": {
15 | "import/resolver": {
16 | "node": {
17 | "extensions": [".js", ".ts", ".tsx"]
18 | }
19 | }
20 | },
21 | "rules": {
22 | "react-hooks/rules-of-hooks": "error",
23 | "@typescript-eslint/explicit-function-return-type": "off",
24 | "react/jsx-filename-extension": ["error", { "extensions": [".js", ".tsx"] }],
25 | "react/prop-types": "off",
26 | "react/jsx-one-expression-per-line": "off",
27 | "import/extensions": ["error", "never"],
28 | "import/prefer-default-export": "off",
29 | "import/no-unresolved": ["error", { "ignore": ["react-suspense-worker"] }],
30 | "@typescript-eslint/no-explicit-any": "off",
31 | "no-use-before-define": "off",
32 | "no-unused-vars": "off",
33 | "react/function-component-definition": ["error", { "namedComponents": "arrow-function" }]
34 | },
35 | "overrides": [{
36 | "files": ["__tests__/**/*"],
37 | "env": {
38 | "jest": true
39 | },
40 | "rules": {
41 | "import/no-extraneous-dependencies": ["error", { "devDependencies": true }]
42 | }
43 | }]
44 | }
45 |
--------------------------------------------------------------------------------
/examples/01_minimal/src/index.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense, useState, useTransition } from 'react';
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import { wrap } from 'react-suspense-worker';
5 |
6 | const fib = wrap(new Worker(new URL('./slow_fib.worker', import.meta.url)));
7 |
8 | const fastFib = (i) => {
9 | if (i <= 1) return i;
10 | let a = 0;
11 | let b = 1;
12 | for (let x = 1; x < i; x += 1) {
13 | [a, b] = [b, a + b];
14 | }
15 | return b;
16 | };
17 |
18 | const DisplayFib = ({ number }) => {
19 | const result = fib(number);
20 | const result2 = fastFib(number);
21 | return (
22 |
23 |
result: {result}
24 |
result2: {result2}
25 |
26 | );
27 | };
28 |
29 | const Main = () => {
30 | const [number, setNumber] = useState(1);
31 | const [isPending, startTransition] = useTransition();
32 | const onClick = () => {
33 | startTransition(() => {
34 | setNumber((c) => c + 1);
35 | });
36 | };
37 | return (
38 |
39 | number: {number}
40 | +1
41 | {isPending && 'Pending...'}
42 |
43 |
44 | );
45 | };
46 |
47 | const App = () => (
48 | Loading...}>
49 |
50 |
51 | );
52 |
53 | createRoot(document.getElementById('app')).render( );
54 |
--------------------------------------------------------------------------------
/src/wrap.ts:
--------------------------------------------------------------------------------
1 | import { wrap as comlinkWrap, Endpoint } from 'comlink';
2 |
3 | const isPromise = (x: unknown): x is Promise => (
4 | !!x && typeof (x as Promise).then === 'function'
5 | );
6 |
7 | const isObject = (x: unknown): x is object => typeof x === 'object' && x !== null;
8 |
9 | const createProxy = (remote: any): any => {
10 | const cacheForGet = new Map();
11 | const cacheForApply = new Map();
12 | return new Proxy(remote, {
13 | get(target, key) {
14 | if (cacheForGet.has(key)) {
15 | const result = cacheForGet.get(key);
16 | // cacheForGet.delete(key);
17 | if (isObject(result)) {
18 | return createProxy(result);
19 | }
20 | return result;
21 | }
22 | const result = target[key];
23 | if (isPromise(result)) {
24 | throw result.then((r) => { cacheForGet.set(key, r); });
25 | }
26 | if (isObject(result)) {
27 | return createProxy(result);
28 | }
29 | return result;
30 | },
31 | apply(target, thisArg, args) {
32 | const key = JSON.stringify(args); // hoping args is small and stringify is fast
33 | if (cacheForApply.has(key)) {
34 | const result = cacheForApply.get(key);
35 | // cacheForApply.delete(key);
36 | if (isObject(result)) {
37 | return createProxy(result);
38 | }
39 | return result;
40 | }
41 | const result = target.apply(thisArg, args);
42 | if (isPromise(result)) {
43 | throw result.then((r) => { cacheForApply.set(key, r); });
44 | }
45 | if (isObject(result)) {
46 | return createProxy(result);
47 | }
48 | return result;
49 | },
50 | });
51 | };
52 |
53 | /**
54 | * Wrap a worker to be used with React Suspense
55 | *
56 | * @example
57 | * import { wrap } from 'react-suspense-worker';
58 | *
59 | * const fib = wrap(new Worker(new URL('./slow_fib.worker', import.meta.url)));
60 | *
61 | * const DisplayFib = ({ number }) => {
62 | * const result = fib(number);
63 | * return result: {result}
;
64 | * };
65 | */
66 | export const wrap = (ep: Endpoint): T => {
67 | const remote = comlinkWrap(ep);
68 | const proxy = createProxy(remote);
69 | return proxy as T;
70 | };
71 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-suspense-worker",
3 | "description": "React Suspense for Web Worker with Comlink",
4 | "version": "0.1.0",
5 | "author": "Daishi Kato",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/dai-shi/react-suspense-worker.git"
9 | },
10 | "source": "./src/index.ts",
11 | "main": "./dist/index.umd.js",
12 | "module": "./dist/index.modern.js",
13 | "types": "./dist/src/index.d.ts",
14 | "exports": {
15 | "./package.json": "./package.json",
16 | ".": {
17 | "types": "./dist/src/index.d.ts",
18 | "module": "./dist/index.modern.js",
19 | "import": "./dist/index.modern.mjs",
20 | "default": "./dist/index.umd.js"
21 | }
22 | },
23 | "sideEffects": false,
24 | "files": [
25 | "src",
26 | "dist"
27 | ],
28 | "scripts": {
29 | "compile": "microbundle build -f modern,umd --globals comlink=Comlink",
30 | "postcompile": "cp dist/index.modern.js dist/index.modern.mjs && cp dist/index.modern.js.map dist/index.modern.mjs.map",
31 | "test": "run-s eslint tsc-test jest",
32 | "eslint": "eslint --ext .js,.ts,.tsx --ignore-pattern dist .",
33 | "jest": "jest",
34 | "tsc-test": "tsc --project . --noEmit",
35 | "apidoc": "documentation readme --section API --markdown-toc false --parse-extension ts --require-extension ts src/*.ts",
36 | "examples:01_minimal": "DIR=01_minimal EXT=js webpack serve",
37 | "examples:02_typescript": "DIR=02_typescript webpack serve"
38 | },
39 | "jest": {
40 | "preset": "ts-jest/presets/js-with-ts"
41 | },
42 | "keywords": [
43 | "react",
44 | "suspense",
45 | "worker"
46 | ],
47 | "license": "MIT",
48 | "dependencies": {
49 | "comlink": "^4.3.1"
50 | },
51 | "devDependencies": {
52 | "@testing-library/react": "^13.3.0",
53 | "@types/jest": "^28.1.1",
54 | "@types/react": "^18.0.12",
55 | "@types/react-dom": "^18.0.5",
56 | "@typescript-eslint/eslint-plugin": "^5.27.1",
57 | "@typescript-eslint/parser": "^5.27.1",
58 | "documentation": "^13.2.5",
59 | "eslint": "^8.17.0",
60 | "eslint-config-airbnb": "^19.0.4",
61 | "eslint-plugin-import": "^2.26.0",
62 | "eslint-plugin-jsx-a11y": "^6.5.1",
63 | "eslint-plugin-react": "^7.30.0",
64 | "eslint-plugin-react-hooks": "^4.5.0",
65 | "html-webpack-plugin": "^5.5.0",
66 | "jest": "^28.1.1",
67 | "microbundle": "^0.15.0",
68 | "npm-run-all": "^4.1.5",
69 | "react": "^18.1.0",
70 | "react-dom": "^18.1.0",
71 | "ts-jest": "^28.0.4",
72 | "ts-loader": "^9.3.0",
73 | "typescript": "^4.7.3",
74 | "webpack": "^5.73.0",
75 | "webpack-cli": "^4.9.2",
76 | "webpack-dev-server": "^4.9.2"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-suspense-worker
2 |
3 | [](https://github.com/dai-shi/react-suspense-worker/actions?query=workflow%3ACI)
4 | [](https://www.npmjs.com/package/react-suspense-worker)
5 | [](https://bundlephobia.com/result?p=react-suspense-worker)
6 | [](https://discord.gg/MrQdmzd)
7 |
8 | React Suspense for Web Worker with Comlink
9 |
10 | ## Introduction
11 |
12 | This is an experimental library to support Web Workers with React Suspense.
13 | Currently, it's implemented with
14 | [Comlink](https://github.com/GoogleChromeLabs/comlink).
15 | Comlink is promise based, but a value wrapped by this library
16 | can be treated as a normal value without async/await.
17 |
18 | Known issues:
19 |
20 | * No way to clear cache
21 | * Class not supported (yet)
22 | * (...and maybe more)
23 |
24 | ## Install
25 |
26 | ```bash
27 | npm install react-suspense-worker
28 | ```
29 |
30 | ## Usage
31 |
32 | slow_fib.worker.js:
33 |
34 | ```javascript
35 | import { expose } from 'react-suspense-worker';
36 |
37 | const fib = i => (i <= 1 ? i : fib(i - 1) + fib(i - 2));
38 |
39 | expose(fib);
40 | ```
41 |
42 | App.jsx:
43 |
44 | ```javascript
45 | import { wrap } from 'react-suspense-worker';
46 |
47 | const fib = wrap(new Worker(new URL('./slow_fib.worker', import.meta.url)));
48 |
49 | const DisplayFib = ({ number }) => {
50 | const result = fib(number);
51 | return result: {result}
;
52 | };
53 |
54 | const Main = () => {
55 | const [number, setNumber] = useState(1);
56 | const [isPending, startTransition] = useTransition();
57 | const onClick = () => {
58 | startTransition(() => {
59 | setNumber((c) => c + 1);
60 | });
61 | };
62 | return (
63 |
64 | number: {number}
65 | +1
66 | {isPending && 'Pending...'}
67 |
68 |
69 | );
70 | };
71 |
72 | const App = () => (
73 | Loading...}>
74 |
75 |
76 | );
77 | ```
78 |
79 | ## API
80 |
81 |
82 |
83 | ### expose
84 |
85 | Expose a value in worker thread to be wrapped in main thread
86 |
87 | #### Examples
88 |
89 | ```javascript
90 | import { expose } from 'react-suspense-worker';
91 |
92 | const fib = (i) => (i <= 1 ? i : fib(i - 1) + fib(i - 2));
93 |
94 | expose(fib);
95 | ```
96 |
97 | ### wrap
98 |
99 | Wrap a worker to be used with React Suspense
100 |
101 | #### Parameters
102 |
103 | * `ep` **Endpoint**
104 |
105 | #### Examples
106 |
107 | ```javascript
108 | import { wrap } from 'react-suspense-worker';
109 |
110 | const fib = wrap(new Worker(new URL('./slow_fib.worker', import.meta.url)));
111 |
112 | const DisplayFib = ({ number }) => {
113 | const result = fib(number);
114 | return result: {result}
;
115 | };
116 | ```
117 |
118 | Returns **T**
119 |
120 | ## Examples
121 |
122 | The [examples](examples) folder contains working examples.
123 | You can run one of them with
124 |
125 | ```bash
126 | PORT=8080 npm run examples:01_minimal
127 | ```
128 |
129 | and open in your web browser.
130 |
131 |
136 |
--------------------------------------------------------------------------------