npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-use-comlink

v2.0.1

Published

React hook for seamless usage with Comlink webworker library

Downloads

60

Readme

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