中文 | English
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
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
- 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.
- Requires you to write event name just like node.js
- 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.
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);
-
type TypedEvent<Payload>
- The type that "createTypedEvent" returns.
-
createTypedEvent<Payload>(dispatchLastValueOnSubscribe?: boolean): TypedEvent<Payload>
Create a new event unit.- Parameter
dispatchLastValueOnSubscribe
optional, default tofalse
.
IfdispatchLastValueOnSubscribe
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.
- Parameter
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>
Aref
that holds the current value of the event, useful for accessing the latest value in effects or callbacks.
- Returns a tuple of:
createTypedEventMemorized<Payload>(dispatchLastValueOnSubscribe?: boolean)
CreateTypedEvent
within react component. Parameters and returns are the same ascreateTypedEvent
.