Skip to content

Commit

Permalink
fix: circular reference destructuring works with all models (#947)
Browse files Browse the repository at this point in the history
  • Loading branch information
sushantdhiman committed Nov 9, 2021
1 parent fdf8fd1 commit 7ada366
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 16 deletions.
23 changes: 17 additions & 6 deletions packages/core/src/dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,14 @@ const createActionDispatcher = <

/**
* Creates a dispatcher object for a model - it contains a mapping from all
* reducers and effects *names* to functions which dispatch their corresponding
* actions.
* reducers to functions which dispatch their corresponding actions.
*/
const createDispatcher = <
export const createReducerDispatcher = <
TModels extends Models<TModels>,
TExtraModels extends Models<TModels>,
TModel extends NamedModel<TModels>
>(
rematch: RematchStore<TModels, TExtraModels>,
bag: RematchBag<TModels, TExtraModels>,
model: TModel
): void => {
const modelDispatcher = rematch.dispatch[model.name]
Expand All @@ -73,7 +71,22 @@ const createDispatcher = <
false
)
})
}

/**
* Creates effects dispatcher for a model - it contains a mapping from all
* effects *names* to functions which dispatch their corresponding actions.
*/
export const createEffectDispatcher = <
TModels extends Models<TModels>,
TExtraModels extends Models<TModels>,
TModel extends NamedModel<TModels>
>(
rematch: RematchStore<TModels, TExtraModels>,
bag: RematchBag<TModels, TExtraModels>,
model: TModel
): void => {
const modelDispatcher = rematch.dispatch[model.name]
let effects: ModelEffects<TModels> = {}

// 'effects' might be actually a function creating effects
Expand All @@ -100,5 +113,3 @@ const createDispatcher = <
)
})
}

export default createDispatcher
36 changes: 26 additions & 10 deletions packages/core/src/rematchStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import createReduxStore, {
createModelReducer,
createRootReducer,
} from './reduxStore'
import createDispatcher from './dispatcher'
import { createReducerDispatcher, createEffectDispatcher } from './dispatcher'
import { validateModel } from './validate'
import createRematchBag from './bag'

Expand All @@ -43,16 +43,24 @@ export default function createRematchStore<
addModel(model: NamedModel<TModels>) {
validateModel(model)
createModelReducer(bag, model)
prepareModel(rematchStore, bag, model)
prepareModel(rematchStore, model)
enhanceModel(rematchStore, bag, model)
reduxStore.replaceReducer(createRootReducer(bag))
reduxStore.dispatch({ type: '@@redux/REPLACE' })
},
} as RematchStore<TModels, TExtraModels>

addExposed(rematchStore, config.plugins)

// generate dispatch[modelName][actionName] for all reducers and effects
bag.models.forEach((model) => prepareModel(rematchStore, bag, model))
/**
* generate dispatch[modelName][actionName] for all reducers and effects
*
* Note: To have circular models accessible in effects method with destructing,
* ensure that model generation and effects generation execute in
* different steps.
*/
bag.models.forEach((model) => prepareModel(rematchStore, model))
bag.models.forEach((model) => enhanceModel(rematchStore, bag, model))

bag.forEachPlugin('onStoreCreated', (onStoreCreated) => {
rematchStore = onStoreCreated(rematchStore, bag) || rematchStore
Expand Down Expand Up @@ -88,18 +96,26 @@ function prepareModel<
TModels extends Models<TModels>,
TExtraModels extends Models<TModels>,
TModel extends NamedModel<TModels>
>(
rematchStore: RematchStore<TModels, TExtraModels>,
bag: RematchBag<TModels, TExtraModels>,
model: TModel
): void {
>(rematchStore: RematchStore<TModels, TExtraModels>, model: TModel): void {
const modelDispatcher = {} as ModelDispatcher<TModel, TModels>

// inject model so effects creator can access it
rematchStore.dispatch[`${model.name}` as keyof RematchDispatch<TModels>] =
modelDispatcher

createDispatcher(rematchStore, bag, model)
createReducerDispatcher(rematchStore, model)
}

function enhanceModel<
TModels extends Models<TModels>,
TExtraModels extends Models<TModels>,
TModel extends NamedModel<TModels>
>(
rematchStore: RematchStore<TModels, TExtraModels>,
bag: RematchBag<TModels, TExtraModels>,
model: TModel
): void {
createEffectDispatcher(rematchStore, bag, model)

bag.forEachPlugin('onModel', (onModel) => {
onModel(model, rematchStore)
Expand Down
45 changes: 45 additions & 0 deletions packages/core/test/v1_regressions/circurlarmodels.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { init } from '../../src'

it('circular models should destruct properly', async () => {
type CountState = number
const dolphins = {
state: 0,
reducers: {
increment: (state: CountState) => state + 1,
},
effects: ({ sharks, dolphins }: any) => {
return {
async incrementAsync(): Promise<void> {
dolphins.increment()
},
async incrementSharksAsync(): Promise<void> {
sharks.incrementAsync(1)
},
}
},
}
const sharks = {
state: 0,
reducers: {
increment: (state: CountState, payload: number) => state + payload,
},
effects: ({ dolphins, sharks }: any) => ({
async incrementAsync(payload: number): Promise<void> {
dolphins.increment()
sharks.increment(payload)
},
}),
}

const store = init({
models: { dolphins, sharks },
})

// store.dispatch({ type: 'count/addCount' })
await store.dispatch.sharks.incrementAsync(4)
expect(store.getState().sharks).toEqual(4)

await store.dispatch.dolphins.increment()
await store.dispatch.dolphins.incrementSharksAsync()
expect(store.getState().dolphins).toEqual(3)
})

0 comments on commit 7ada366

Please sign in to comment.