Skip to content

Function-based and directive tiny typed event, with current value and optional react hook. Proven and well-tested in private projects. 全功能、函数式调用的微型事件库,可读取当前值且带有可选 react hook。在内部项目中久经考验。

License

Notifications You must be signed in to change notification settings

Misaka-0x447f/createTypedEvent

Repository files navigation

create-typed-event

NPM Version npm bundle size

中文 | English

Typed . Autocomplete . One call unsubscribe . React is Optional

A simple eventManager, with built-in react support.
Best practices refined over five years. Proven and well-tested in private projects.

......And seriously, what's so bad about jumper wiring?

History on gist: https://gist.github.com/Misaka-0x447f/0c37018ae7bd944cbff54d27b6d4fd9f

Compares

typed easy to use perf autocomplete one call unsub no React latestValue
create-typed-event
redux *️⃣ 1 *️⃣ 5
mitt *️⃣ 2
jotai *️⃣ 3 *️⃣ 8
valtio *️⃣ 4 *️⃣ 6
rxjs *️⃣ 7

*1: Reducer required
*2: All key shares same type
*3: Two step create, atom & store
*4: Proxy based, which means just like vue 3, has it's trace limitation.
*5: Heavy slice
*6: v8 may not play well with proxy
*7: Really heavy even with rxjs subject
*8: Need to use default store

Detailed compares

  • redux
    • Toooooooooooo complex. To emit a state you have to create a new reducer, with a new store, a string store name...
  • mitt / nodejs EventEmitter
    • Requires you to write event name just like node.js EventEmitter: emitter.on('xxx'..., which is not friendly to autocomplete. And why not put key in payload?
    • Does not return an unsub method.
    • Does not support getting current value.
  • jotai
    • To create a equivalent, you have to create an atom, then a store. And it requires user to create store every time user needs to subscribe.
    • Does not support getting current value outside react context. See jotai issue
  • valtio
    • A good solution, but powered by Proxy, which is not really efficient and not working with complex object.
  • rxjs
    • Its Objective: requires you to create an Observable/Subject object, with the "new" statement we hate.
    • Way much larger package size and more complex API.
  • so many others
    • Almost every library I know requires you to write event name on subscription, and cannot auto-complete.
    • So I trust the solution I made can be unique and useful.

Examples

Traditional unsubscribe:

import { createTypedEvent } from '@misaka17535/create-typed-event'

type Payload = { ready: boolean }
const networkStateChange = createTypedEvent<Payload>()

const handler = (payload: Payload) => console.log(payload)
networkStateChange.sub(handler)
networkStateChange.dispatch({ready: true})
networkStateChange.unsub(handler)

>>> { ready: true }

Simplified unsubscribe:

import { createTypedEvent } from '@misaka17535/create-typed-event'

const misakaStateChange = createTypedEvent<{ selfDestructionInProgress: boolean }>()
const unsub = misakaStateChange.sub((payload) => console.log(payload)) // returns unsub function without defining handler outside
misakaStateChange.dispatch({selfDestructionInProgress: true})
unsub()

>>> { selfDestructionInProgress: true }

Create an "event bus":

import { createTypedEvent } from '@misaka17535/create-typed-event'

export const eventBus = {
    alice: createTypedEvent(),
    bob: createTypedEvent<{ isE2eEncryption: boolean }>()
}
eventBus.bob.dispatch({isE2eEncryption: true})

Supports react hook:

import { createTypedEventMemorized } from '@misaka17535/create-typed-event/react'
const marketPriceUpdateEvent = createTypedEventMemorized<number>();

import { useTypedEventValue } from '@misaka17535/create-typed-event/react'
const [marketPrice, setMarketPrice, marketPriceRef] = useTypedEventValue(marketPriceUpdateEvent);

API

import { createTypedEvent, type TypedEvent } from '@misaka17535/create-typed-event'

  • type TypedEvent<Payload>

    • The type that "createTypedEvent" returns.
  • createTypedEvent<Payload>(dispatchLastValueOnSubscribe?: boolean): TypedEvent<Payload> Create a new event unit.

    • Parameter
      • dispatchLastValueOnSubscribe optional, default to false.
        If dispatchLastValueOnSubscribe is true, it will dispatch the last value to new subscribers.
    • Returns an object with the following:
      • sub(callback: (payload: Payload) => void): () => void
        Subscribe to event. Returns an unsub method that does not require original callback.
      • unsub(callback: (payload: Payload) => void): void Unsubscribe from event with the original callback.
      • dispatch(payload: Payload): void
        Dispatch an event with the given payload.
      • once(callback: (payload: Payload) => void): () => void
        Subscribe to event, but only once. Returns an unsub method that does not require original callback.
      • get value(): Payload | undefined
        A getter to get the current value of the event.

import { useTypedEvent, createTypedEventMemorized } from '@misaka17535/create-typed-event/react'

  • useTypedEvent(event: TypedEvent): HybridState Use typed event as a React state.
    • Returns a tuple of:
      • value: Payload | undefined
        The current value of the event.
      • setValue: (payload: Payload) => void
        A function to set the value of the event.
      • ref: React.MutableRefObject<Payload | undefined>
        A ref that holds the current value of the event, useful for accessing the latest value in effects or callbacks.
  • createTypedEventMemorized<Payload>(dispatchLastValueOnSubscribe?: boolean) Create TypedEvent within react component. Parameters and returns are the same as createTypedEvent.

About

Function-based and directive tiny typed event, with current value and optional react hook. Proven and well-tested in private projects. 全功能、函数式调用的微型事件库,可读取当前值且带有可选 react hook。在内部项目中久经考验。

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published