diff --git a/src/index.d.ts b/src/index.d.ts index f585586..57e0212 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,14 +1,32 @@ declare module "use-bus" { - export interface EventAction { - type: string; + export interface EventAction { + type: T; [key: string]: any; } - export function dispatch(name: string): void; - export function dispatch(event: EventAction): void; + interface DispatchFn { + (name: keyof T): void; + (event: T): void; + } + + type FilterActionType< + A extends EventAction, + ActionType extends A["type"] + > = A extends any ? ActionType extends A['type'] ? A : never : never; + + interface UseBus< + T extends EventAction = EventAction + > { + (name: TName, callback: (event: FilterActionType) => void, deps: any[]): DispatchFn; + (name: TName[], callback: (event: FilterActionType) => void, deps: any[]): DispatchFn; + (name: RegExp, callback: (event: T) => void, deps: any[]): DispatchFn; + (filter: (event: T) => event is TEvent, callback: (event:TEvent) => void, deps: any[]): DispatchFn; + } + + export const dispatch: DispatchFn; + + const useBus: UseBus; + export default useBus; - export default function useBus(name: string, callback: (event: EventAction) => void, deps: any[]): typeof dispatch; - export default function useBus(name: string[], callback: (event: EventAction) => void, deps: any[]): typeof dispatch; - export default function useBus(name: RegExp, callback: (event: EventAction) => void, deps: any[]): typeof dispatch; - export default function useBus(filter: (event: EventAction) => boolean, callback: (event: EventAction) => void, deps: any[]): typeof dispatch; + export function createBus(): [UseBus, DispatchFn]; } diff --git a/src/index.js b/src/index.js index 2e60ce1..b58efd6 100644 --- a/src/index.js +++ b/src/index.js @@ -1,43 +1,50 @@ import { useEffect } from 'react' -let subscribers = [] +export const createBus = () => { + let subscribers = [] -const subscribe = (filter, callback) => { - if (filter === undefined || filter === null) return undefined - if (callback === undefined || callback === null) return undefined + const subscribe = (filter, callback) => { + if (filter === undefined || filter === null) return undefined + if (callback === undefined || callback === null) return undefined - subscribers = [ - ...subscribers, - [filter, callback], - ] + subscribers = [ + ...subscribers, + [filter, callback], + ] - return () => { - subscribers = subscribers.filter((subscriber) => subscriber[1] !== callback) + return () => { + subscribers = subscribers.filter((subscriber) => subscriber[1] !== callback) + } } -} -export const dispatch = (event) => { - let { type } = event - if (typeof event === 'string') type = event + const dispatch = (event) => { + let { type } = event + if (typeof event === 'string') type = event - const args = [] - if (typeof event === 'string') args.push({ type }) - else args.push(event) + const args = [] + if (typeof event === 'string') args.push({ type }) + else args.push(event) - subscribers.forEach(([filter, callback]) => { - if (typeof filter === 'string' && filter !== type) return - if (Array.isArray(filter) && !filter.includes(type)) return - if (filter instanceof RegExp && !filter.test(type)) return - if (typeof filter === 'function' && !filter(...args)) return + subscribers.forEach(([filter, callback]) => { + if (typeof filter === 'string' && filter !== type) return + if (Array.isArray(filter) && !filter.includes(type)) return + if (filter instanceof RegExp && !filter.test(type)) return + if (typeof filter === 'function' && !filter(...args)) return - callback(...args) - }) -} + callback(...args) + }) + } -const useBus = (type, callback, deps = []) => { - useEffect(() => subscribe(type, callback), deps) + const useBus = (type, callback, deps = []) => { + useEffect(() => subscribe(type, callback), deps) - return dispatch + return dispatch + } + + return [useBus, dispatch] } +const [useBus, dispatch] = createBus(); + +export { dispatch } export default useBus diff --git a/src/index.spec.js b/src/index.spec.js index abc5c4f..4521510 100644 --- a/src/index.spec.js +++ b/src/index.spec.js @@ -1,6 +1,6 @@ /* eslint-env jest */ import { renderHook } from '@testing-library/react-hooks' -import useBus, { dispatch } from './index' +import useBus, { dispatch, createBus } from './index' const prepare = (done) => (renderHookResult) => () => { renderHookResult.unmount() @@ -177,3 +177,34 @@ it('should register a RegExp and call the callback', (done) => { done() }) + +describe('multiple buses', () => { + const [useBusA, dispatchA] = createBus() + const [useBusB, dispatchB] = createBus(); + + it('should not cross publish', () => { + let aCount = 0; + let bCount = 0; + + const { unmount: unmountA } = renderHook(() => useBusA('EVENT_TYPE', (evt) => { + aCount += 1; + expect(evt).toEqual({ type: 'EVENT_TYPE' }) + })) + + const { unmount: unmountB } = renderHook(() => useBusB('EVENT_TYPE', (evt) => { + bCount += 1; + expect(evt).toEqual({ type: 'EVENT_TYPE' }) + })) + + dispatchA('EVENT_TYPE') + expect(aCount).toEqual(1) + expect(bCount).toEqual(0) + + dispatchB('EVENT_TYPE') + expect(aCount).toEqual(1) + expect(bCount).toEqual(1) + + unmountA() + unmountB() + }) +})