Skip to content

web

This package exposes a set of handy bindings to browser APIs.

Installation

#
Terminal window
npm i @reatom/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 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:

import { atom, onConnect, onCtxAbort } from '@reatom/framework'
import { onEvent } from '@reatom/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 you should use checkpoints to handle all events without skipping it.

import { reatomAsync } from '@reatom/async'
import { onEvent } from '@reatom/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:

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)
})