Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions __test__/middlewares/subscribe.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ describe('Subscribe middleware', () => {
let actions: any
let count = 0
const { useStore, subscribe } = Model({ Counter })
subscribe('Counter', 'increment', () => (count += 1))
subscribe('Counter', ['increment'], () => (count += 1))
subscribe('Counter', 'add', () => (count += 10))
subscribe('Counter', ['increment', 'add'], () => (count += 5))
testHook(() => {
;[, actions] = useStore('Counter')
})
await actions.increment()
await actions.add(1)
await actions.increment()
await actions.increment()
expect(count).toBe(13)
expect(count).toBe(33)
})
})
26 changes: 26 additions & 0 deletions __test__/middlewares/unsubscribe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/// <reference path="../index.d.ts" />
import 'react-testing-library/cleanup-after-each'
import { Model } from '../../src'
import { Counter } from '..'
import { testHook } from 'react-hooks-testing-library'

describe('Subscribe middleware', () => {
test('run callback when specific action run', async () => {
let actions: any
let count = 0
const { useStore, subscribe, unsubscribe } = Model({ Counter })
subscribe('Counter', ['increment'], () => (count += 1))
subscribe('Counter', 'add', () => (count += 10))
subscribe('Counter', ['increment', 'add'], () => (count += 5))
testHook(() => {
;[, actions] = useStore('Counter')
})
await actions.increment()
unsubscribe('Counter', 'add')
await actions.add(1)
unsubscribe('Counter', ['add', 'increment'])
await actions.increment()
await actions.increment()
expect(count).toBe(6)
})
})
2 changes: 1 addition & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,5 +126,5 @@ type ModelsProps<M extends Models> = {
}

type Subscriptions = {
[key: string]: Function
[key: string]: Function[]
}
59 changes: 53 additions & 6 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,27 @@ const Model = <M extends Models>(models: M, initialState?: Global['State']) => {
Global.AsyncState[name] = model.asyncState
})

const actions = Object.keys(models).reduce(
(o, modelName) => ({ ...o, name: getActions(modelName) }),
{}
)

Global.withDevTools =
typeof window !== 'undefined' &&
(window as any).__REDUX_DEVTOOLS_EXTENSION__
if (Global.withDevTools) {
Global.devTools = (window as any).__REDUX_DEVTOOLS_EXTENSION__
Global.devTools.connect()
}
return { useStore, getState, getInitialState, getActions, subscribe } as {
return {
actions,
useStore,
getState,
getInitialState,
getActions,
subscribe,
unsubscribe
} as {
useStore: <K extends keyof M>(
name: K,
depActions?: (keyof Get<M[K], 'actions'>)[]
Expand All @@ -39,25 +52,59 @@ const Model = <M extends Models>(models: M, initialState?: Global['State']) => {
getInitialState: typeof getInitialState
subscribe: <K extends keyof M>(
modelName: K,
actionName: keyof Get<M[K], 'actions'> | keyof Get<M[K], 'actions'>,
actionName: keyof Get<M[K], 'actions'> | (keyof Get<M[K], 'actions'>)[],
callback: Function
) => void
unsubscribe: <K extends keyof M>(
modelName: K,
actionName: keyof Get<M[K], 'actions'> | (keyof Get<M[K], 'actions'>)[]
) => void
actions: {
[K in keyof M]: Readonly<getConsumerActionsType<Get<M[K], 'actions'>>>
}
}
}

const unsubscribe = (modelName: string, actions: string | string[]) => {
subscribe(modelName, actions, undefined)
}

const subscribe = (
modelName: string,
actionName: string,
callback: Function
actions: string | string[],
callback?: Function
) => {
Global.subscriptions[`${modelName}_${actionName}`] = callback
if (Array.isArray(actions)) {
actions.forEach(actionName => {
if (!Global.subscriptions[`${modelName}_${actionName}`]) {
Global.subscriptions[`${modelName}_${actionName}`] = []
}
if (callback) {
Global.subscriptions[`${modelName}_${actionName}`].push(callback)
} else {
Global.subscriptions[`${modelName}_${actionName}`] = []
}
})
} else {
if (!Global.subscriptions[`${modelName}_${actions}`]) {
Global.subscriptions[`${modelName}_${actions}`] = []
}
if (callback) {
Global.subscriptions[`${modelName}_${actions}`].push(callback)
} else {
Global.subscriptions[`${modelName}_${actions}`] = []
}
}
}

const getState = (modelName: keyof typeof Global.State) => {
return Global.State[modelName]
}

const getActions = (modelName: string, baseContext: Partial<Context>) => {
const getActions = (
modelName: string,
baseContext: Partial<Context> = { type: 'outer' }
) => {
const updaters: any = {}
Object.entries(Global.Actions[modelName]).forEach(
([key, action]) =>
Expand Down
8 changes: 5 additions & 3 deletions src/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ const stateUpdater: Middleware = async (context, restMiddlewares) => {

const subscription: Middleware = async (context, restMiddlewares) => {
const { modelName, actionName, next, Global } = context
if (Global.subscriptions[`${modelName}_${actionName}`]) {
Global.subscriptions[`${modelName}_${actionName}`]()
}
const subscriptions = Global.subscriptions[`${modelName}_${actionName}`]
subscriptions &&
subscriptions.forEach(callback => {
callback()
})
await next(restMiddlewares)
}

Expand Down