GithubHelp home page GithubHelp logo

dai-shi / react-hooks-worker Goto Github PK

View Code? Open in Web Editor NEW
699.0 10.0 17.0 2.5 MB

React custom hooks for web workers

License: MIT License

JavaScript 20.67% TypeScript 79.33%
react reactjs react-hooks worker webworker custom-hook

react-hooks-worker's Introduction

react-hooks-worker

CI npm size discord

React custom hooks for web workers.

Introduction

Web Workers are another thread from the main thread in browsers. We can run heavy computation in a separate thread so that users don't feel slowing down.

React provides a reactive system. This library hides the async nature of Web Workers with React custom hooks. Results returned by Web Workers are stored in a React local state.

Developers can implement a worker as:

  • sync function
  • async function
  • sync generator function
  • async generator function

Install

npm install react-hooks-worker

Usage

slow_fib.worker.js:

import { exposeWorker } from 'react-hooks-worker';

const fib = i => (i <= 1 ? i : fib(i - 1) + fib(i - 2));

exposeWorker(fib);

app.js:

import React from 'react';

import { useWorker } from 'react-hooks-worker';

const createWorker = () => new Worker(
  new URL('./slow_fib.worker', import.meta.url),
  { type: 'module' }
);

const CalcFib = ({ count }) => {
  const { result, error } = useWorker(createWorker, count);
  if (error) return <div>Error: {error}</div>;
  return <div>Result: {result}</div>;
};

const App = () => (
  <div>
    <CalcFib count={5} />
  </div>
);

Recipes

Pending status

The communication between main thread and worker thread is not RPC model. It can be one input to return multiple outputs, or multiple inputs to get one output.

Handling pending or stale status is left for library users. Refer #44 for a recipe for isStale.

API

exposeWorker

expose worker

You can expose any function that returns:

  • A value
  • A promise
  • An iterable
  • An async iterable

Parameters

  • func function (data: any): any
  • getOptions function (): WindowPostMessageOptions?

Examples

import { exposeWorker } from 'react-hooks-worker';

const fib = (i) => (i <= 1 ? i : fib(i - 1) + fib(i - 2));

exposeWorker(fib);

useWorker

use worker

The createWorker function should be stable to keep the worker running. If it's referentially changed, it will create a new worker and terminate the old one.

Parameters

  • createWorker function (): Worker
  • input Input
  • getOptions function (): WindowPostMessageOptions?

Examples

import { useWorker } from 'react-hooks-worker';

const createWorker = () => new Worker(
  new URL('./slow_fib.worker', import.meta.url),
  { type: 'module' }
);

const CalcFib = ({ count }) => {
  const { result, error } = useWorker(createWorker, count);
  if (error) return <div>Error: {error}</div>;
  return <div>Result: {result}</div>;
};

Examples

The examples folder contains working examples. You can run one of them with

PORT=8080 npm run examples:01_minimal

and open http://localhost:8080 in your web browser.

Blogs

react-hooks-worker's People

Contributors

dai-shi avatar dangdennis avatar dependabot[bot] avatar relferreira avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-hooks-worker's Issues

Page crash when the 'input' of useWorker is an Object

I want to pass a parameter of type object to the worker. The documentation says that the 'input' of useWorker can be 'any' type. But when I do this, the worker is called repeatedly until the page crashes. Here's a crashed version of the code.

The worker:

import { exposeWorker } from 'react-hooks-worker';

const fib = (i) => (i <= 1 ? i : fib(i - 1) + fib(i - 2));
const fibWrapper = (o) => {
    console.log(o)
    return fib(o.i)
}
exposeWorker(fibWrapper);

The App.js:

const createWorker = () =>
  new Worker(new URL("./slow_fib.worker", import.meta.url));

const CalcFib = ({ count }) => {
  const { result, error } = useWorker(createWorker, { i: count }); // now the input is wrapper in an object
  if (error) return <div>Error: {error}</div>;
  return <div>Result: {result}</div>;
};

const App = () => {
  const { result, error } = useWorker(createWorker, { a: 1 });
  return (
      <CalcFib count={10}/>
)

The console keeps flushing {i: 10}.

Worker sends error no matter what.

I'm not sure what's going wrong. I coped the slow_fib.js from the examples. and am linking to it like this:
let { result, error } = useWorker('./slow_fib.js', 29);

All I am getting is null for result and 'error' for error. I commented out the whole file, and got the same thing...

Performance benchmarks

What's the perf like on cold-starting workers. Your Fib example shows visibility faster calculations compared to normal JS implementations.

Would love to see a perf benchmark - very interested in using this in production

Stale data returning when inputs changed

I believe I found a bug. When I pass in a value to a useWorker that relies on a param, the result appears to be cached. It's hard to explain... for the following code:

const {result} = useWorker(createExpensiveFetch, someParam)
console.log(someParam, result)

It produces console logs like this...

123, { a: 'Hello World', b: 123 },

Then on change of someParam to be 124, I see:

124, { a: 'Hello World', b: 123 },
124, { a: 'Foo Bar', b: 124 },

Basically, I have a hook that takes a prop that can change, and it takes tome to process the worker data (think a data fetch)... but when the param changes, the result/error values seem to be the ones corresponding to the previous param. I've gotten around it by making sure the value of result.b matches someParam, but this seems like a bug?

How about sharedworkers?

Please does this module support shared workers in reactjs?
if yes, please demo or link for demo. Also, need help with shared workers for react app. Thanks

Unable to call external modules inside a workerized function

Issue

I realize that you can’t inline useWorker(function) if function uses imported modules and isn’t written in its own file. Basically, having side-effects in your functions. Webpack doesn’t seem to compile the dependencies as-is.

Error

Example error: js_cookie__WEBPACK_IMPORTED_MODULE_7___default

js-cookie being the npm module.

Solution

  1. Add documentation to warn users that you must move web worker functions into their own files with their own imports.
  2. Maybe even explicitly throw Errors

Parcel Bundler Support

Parcel allow Web Worker scripts to be automatically bundled. However, this only works when you create the Worker instance yourself.

The useWorker effect receives an URL as the first parameter. My suggestion is to support either an URL or an instance of a worker so that this library can be used by webpack and parcel projects

I will submit an PR shortly with my suggested modifications.

Thank you for the work on this project and sorry if I did something wrong

Error when using async function and exposeWorker

I am currently getting an error when trying to use the async pattern in a worker to be exposed, and I am not sure I understand why it happens. Does the library not support an async function? I thought that was included under returning a promise. Maybe I'm misusing this? Please advise.

The error is below:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'Symbol(Symbol.asyncIterator)')
    at self.onmessage (exposeWorker.ts:23)

This snippet is an example of the such.

...
import { exposeWorker } from 'react-hooks-worker'
...
...
const fetchAndTransform = async (someUrl) => {
  const response = await fetch(someUrl)
  const result = someExpensiveTransformFunction(response)

  return result
}

exposeWorker(fetchAndTransform)

Dependency array

Hi,

Just giving this library a try and wondering if there is a way to provide a dependency array like useEffect?

TypeScript definitions

We're using this for a project in TypeScript, and noticed there are no permissible return types for the result object, as it is set to unknown

result?: unknown;

Could we make a change here to allow devs to be more specific?
For example

type State<T extends unknown> = {
  result?: T 
  error?: 'error' | 'messageerror'
}

const initialState: State<any> = {}

Then we can define useWorker as follows:

export function useWorker<T, K>(
  createWorker: () => Worker,
  input: T
): State<K> {
  const [state, setState] = useState<State<K>>(initialState)
  ...

and usages can be like:

 const { result, error } = useWorker<number, number>(createWorker, count);

or

 const { result, error } = useWorker<SimpleType, ComplexType>(createWorker, simpleInput);
// result will be of type ComplexType

This would allow us to know the type of the result object more clearly.

Thoughts are welcome

Alternative to string-based function

Filing this issue because it is in my todo list.

Reported:

  • can't use external function #6
  • warning about inline function #8

I'd like to find a way to make it more practical.
Maybe, it needs to cooperate with bundlers.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.