All Projects → pocesar → react-use-comlink

pocesar / react-use-comlink

Licence: MIT license
Three ways to use Comlink web workers through React Hooks (and in a typesafe manner).

Programming Languages

typescript
32286 projects
javascript
184084 projects - #8 most used programming language
HTML
75241 projects

Projects that are alternatives of or similar to react-use-comlink

useAudioPlayer
Custom React hook & context for controlling browser audio
Stars: ✭ 176 (+351.28%)
Mutual labels:  hooks, react-hooks, react-hook
Thread
type safe multi-threading made easier
Stars: ✭ 34 (-12.82%)
Mutual labels:  worker, webworker, webworkers
React Form
⚛️ Hooks for managing form state and validation in React
Stars: ✭ 2,270 (+5720.51%)
Mutual labels:  hooks, react-hooks
Fre
👻 Tiny Footprint Concurrent UI library for Fiber.
Stars: ✭ 3,195 (+8092.31%)
Mutual labels:  hooks, react-hooks
web
React hooks done right, for browser and SSR.
Stars: ✭ 940 (+2310.26%)
Mutual labels:  react-hooks, react-hook
Graphql Hooks
🎣 Minimal hooks-first GraphQL client
Stars: ✭ 1,610 (+4028.21%)
Mutual labels:  hooks, react-hooks
Haunted
React's Hooks API implemented for web components 👻
Stars: ✭ 2,197 (+5533.33%)
Mutual labels:  hooks, react-hooks
react-ui-hooks
🧩Simple repository of React hooks for building UI components
Stars: ✭ 20 (-48.72%)
Mutual labels:  hooks, react-hooks
Beautiful React Hooks
🔥 A collection of beautiful and (hopefully) useful React hooks to speed-up your components and hooks development 🔥
Stars: ✭ 5,242 (+13341.03%)
Mutual labels:  hooks, react-hooks
react-use-scroll-position
A react hook to use scroll position
Stars: ✭ 45 (+15.38%)
Mutual labels:  react-hooks, react-hook
usehooks-ts
React hook library, ready to use, written in Typescript.
Stars: ✭ 2,873 (+7266.67%)
Mutual labels:  react-hooks, react-hook
veact
Mutable state enhancer library for React based on @vuejs
Stars: ✭ 62 (+58.97%)
Mutual labels:  react-hooks, react-hook
Awesome React Hooks
Awesome React Hooks
Stars: ✭ 7,616 (+19428.21%)
Mutual labels:  hooks, react-hooks
Formik
Build forms in React, without the tears 😭
Stars: ✭ 29,047 (+74379.49%)
Mutual labels:  hooks, react-hooks
Useworker
⚛️ useWorker() - A React Hook for Blocking-Free Background Tasks
Stars: ✭ 2,233 (+5625.64%)
Mutual labels:  hooks, web-workers
The Platform
Web. Components. 😂
Stars: ✭ 4,355 (+11066.67%)
Mutual labels:  hooks, react-hooks
use-algolia
Dead-simple React hook to make Algolia search queries. Supports pagination out of the box.
Stars: ✭ 29 (-25.64%)
Mutual labels:  react-hooks, react-hook
React Hook Form
📋 React Hooks for form state management and validation (Web + React Native)
Stars: ✭ 24,831 (+63569.23%)
Mutual labels:  hooks, react-hooks
Easy Peasy
Vegetarian friendly state for React
Stars: ✭ 4,525 (+11502.56%)
Mutual labels:  hooks, react-hooks
use-async-resource
A custom React hook for simple data fetching with React Suspense
Stars: ✭ 92 (+135.9%)
Mutual labels:  react-hooks, react-hook

NPM TypeScript

react-use-comlink

Three ways to use Comlink web workers through React Hooks (and in a typesafe manner).

Usage

// worker.ts
import { expose } from 'comlink'

export class MyClass {
  private _counter: number

  constructor(init: number) {
    this._counter = init
  }

  get counter() {
    return this._counter
  }

  increment(delta = 1) {
    this._counter += delta
  }
}

expose(MyClass)
// index.ts
import React from 'react'
import useComlink from 'react-use-comlink'
import { WorkerClass } from './worker'

const App: React.FC<{startAt: number}> = (props) => {
  const [state, setState] = React.useState(0)

  const { proxy } = useComlink<typeof WorkerClass>(
    () => new Worker('./worker.ts'),
    [ props.startAt ] // used to recreate the worker if it change, defaults to []
  )

  React.useEffect(() => {
    (async () => {
      // methods, constructors and setters are async
      const classInstance = await new proxy(0)

      await classInstance.increment(1)

      // even getters are asynchronous, regardless of type
      setState(await classInstance.counter)
    })()
  }, [proxy])

  return (
    <div>{state}</div>
  )
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
)

Also notice that the worker property is also exposed, so you may use the library directly with workers without having to use Comlink (kinda defeats the purpose, but oh well):

const App = () => {
  const { worker } = useComlink('./worker.js')

  useEffect(() => {
    worker.onmessage = (e) => {
      /*do stuff*/
    }

    worker.onerror = (e) => {
      /*do stuff*/
    }
  }, [worker])

  const callback = useCallback(() => {
    worker.postMessage('wow')
  }, [worker])

  return (<button onClick={callback}>Post WOW</button>)
}

API

The api is pretty straightforward, you have the in loco useComlink, the factory counter part createComlink and the singleton counter part createComlinkSingleton.

useComlink<T = unknown>(initWorker: Blob | string | () => Worker | string | Blob, deps: any[]): { proxy, worker }

Use directly inside components. Both object and properties are memoized and can be used as deps.

const MyComponent: React.FC = () => {
  const { proxy, worker } = useComlink(() => new Worker('./worker.js'), [deps])
}

createComlink<T = unknown>(initWorker: () => Worker | string | Blob, options = {}): () => { proxy, worker }

Creates a factory version that can spawn multiple workers with the same settings

// outside, just like react-cache, createResource
const useNumber = createComlink<number>(
  () => new Worker('worker.js')
)

const MyComponent: React.FC = () => {
  const { proxy, worker } = useNumber() // call like a hook

  useEffect(() => {
    (async () => {
      const number = await proxy
      // use number
    })()
  }, [proxy])

  return null
}

createComlinkSingleton<T = unknown>(initWorker: Worker, options: WorkerOptions = {}): () => { proxy, worker }

If you want to keep the same state between multiple components, be my guest. Not the best choice for modularity, but hey, I just make the tools. Notice that the worker is never terminated, and must be done on demand (on worker.terminate())

const useSingleton = createComlinkSingleton<() => Bad>(new Worker('./bad.idea.worker.js'))

const MyComponent: React.FC = () => {
  const { proxy } = useSingleton()

  useEffect(() => {
    (async () => {
      const isBad = await proxy()
    })()
  }, [proxy])

  return null
}

Comlink

Make sure the read the Comlink documentation, being the most important part what can be structure cloned

Caveats

Every function with Comlink is async (because you're basically communicating to another thread through web workers), even class instantiation (using new), so local state can't be retrieved automatically from the exposed class, object or function, having to resort to wrapping some code with self invoking async functions, or relying on .then(). If your render depends on the external worker result, you'll have to think of an intermediate state.

Although the worker will be terminated when the component is unmounted, your code might try to "set" the state of an unmounted component because of how workers work (:P) on their own separate thread in a truly async manner.

In the future, when react-cache and Concurrent Mode is upon us, this library will be updated to work nicely with Suspense and the async nature of Comlink

Example

Run npm run example from root then open http://localhost:1234

TODO

Write tests (hardcore web workers tests)

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].