@reatom/web
Version:
Reatom for web
95 lines (68 loc) • 3.35 kB
Markdown
This package exposes a set of handy bindings to browser APIs.
## Installation
```sh
npm i /web
```
## onEvent
The `onEvent` function enables you to respond to various types of events for the target element that supports the `addEventListener` interface, such as `HTMLInputElement` or `WebSocket`, among others.
You can pass a callback as the last argument. In this case, the method will return an unsubscribe function. If you skip the callback, the returned value will be a promise that will resolve with the event.
Please note that this API handles the abort context from the [onConnect](https://www.reatom.dev/package/hooks/#onconnect) effect and other Reatom APIs. It enables you to describe complex logic in a concise and clear manner with memory safety underneath.
### onEvent WebSocket example
Here is a usage example, which was derived from [this observable example](https://github.com/domfarolino/observable/blob/c232b2e585b71a61034fd23ba4337570b537ef27/README.md?plain=1#L86):
```ts
import { atom, onConnect, onCtxAbort } from '/framework'
import { onEvent } from '/web'
const socket = new WebSocket('wss://example.com')
const reatomStock = (ticker) => {
const stockAtom = atom(null, `${ticker}StockAtom`)
onConnect(stockAtom, async (ctx) => {
if (socket.readyState !== WebSocket.OPEN) {
await onEvent(ctx, socket, 'open')
}
socket.send(JSON.stringify({ ticker, type: 'sub' }))
onEvent(ctx, socket, 'message', (event) => {
if (event.data.ticker === ticker) stockAtom(ctx, JSON.parse(event.data))
})
onEvent(ctx, socket, 'close', () => ctx.controller.abort())
onEvent(ctx, socket, 'error', () => ctx.controller.abort())
onCtxAbort(ctx, () =>
socket.send(JSON.stringify({ ticker, type: 'unsub' })),
)
})
return stockAtom
}
const googStockAtom = reatomStock('GOOG')
ctx.subscribe(googStockAtom, updateView)
```
## onEvent checkpoint example
Make sure to listen to event before you actually need it. As in [take](https://reatom.dev/package/effects/#take-checkpoints) you should use checkpoints
to handle all events without skipping it.
```ts
import { reatomAsync } from '/async'
import { onEvent } from '/web'
import { heroAnimation } from '~/feature/hero'
import { api } from '~/api'
const heroElement = document.getElementById('#hero')
const loadPageContent = reatomAsync(async (ctx)=>{
// Docs: https://developer.mozilla.org/en-US/docs/Web/API/Element/animate
const animation = heroElement.animate(heroAnimation)
const content = await api.fetchContent()
// ❌ Bug:
// If person's connection is not fast enough animation can finish before we load content.
// And we will be showing last frame of animation forever...
await onEvent(ctx, animation, 'finish')
pageContent(ctx, content)
})
```
And that's how we fix this behaviour using checkpoint:
```ts
const loadPageContent = reatomAsync(async (ctx)=>{
const animation = heroElement.animate(heroAnimation)
// ✅ We make a checkpoint before loading...
const animationFinishedCheckpoint = onEvent(ctx, animation, 'finish')
const content = await api.fetchContent()
// ...and we will catch that event even if content loading takes ages
await animationFinishedCheckpoint
pageContent(ctx, content)
})
```