Skip to content

Commit 14f0ae7

Browse files
committed
Refactor Module Store to create module Enhancer, this will allow enhancing OOB redux stores with module capabilities.
1 parent 520fd8f commit 14f0ae7

File tree

17 files changed

+222
-121
lines changed

17 files changed

+222
-121
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,10 @@ export function getUsersModule(): IModule<IUserState> {
6464
* Create a `ModuleStore`
6565

6666
```typescript
67-
import {configureStore, IModuleStore} from "redux-dynamic-modules";
67+
import {createStore, IModuleStore} from "redux-dynamic-modules";
6868
import {getUsersModule} from "./usersModule";
6969

70-
const store: IModuleStore<IState> = configureStore(
70+
const store: IModuleStore<IState> = createStore(
7171
/* initial state */
7272
{},
7373

docs/GettingStarted.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ export function getUsersModule(): IModule<IUserState> {
3030
* Create a `ModuleStore`
3131

3232
```typescript
33-
import {configureStore, IModuleStore} from "redux-dynamic-modules";
33+
import {createStore, IModuleStore} from "redux-dynamic-modules";
3434
import {getUsersModule} from "./usersModule";
3535

36-
const store: IModuleStore<IState> = configureStore(
36+
const store: IModuleStore<IState> = createStore(
3737
/* initial state */
3838
{},
3939

docs/reference/Extensions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ export interface IExtension {
2121
```
2222

2323
## Adding extensions to the store
24-
To add an extension to the `ModuleStore`, pass it as the second argument to `configureStore`
24+
To add an extension to the `ModuleStore`, pass it as the second argument to `createStore`
2525
```typescript
26-
const store: IModuleStore<IState> = configureStore({}, [getMyExtension()])
26+
const store: IModuleStore<IState> = createStore({}, [getMyExtension()])
2727
```
2828

2929

docs/reference/ModuleStore.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ The **Module Store** is a Redux store with the added capability of managing **Re
55
* `addModules(modules: IModule<any>[])`: Same as `addModule`, but for multiple modules. The return function will remove all the added modules.
66
* `dispose()`: Remove all of the modules added to the store and dispose of the object
77

8-
To create a `ModuleStore`, use the `configureStore` function from our package
8+
To create a `ModuleStore`, use the `createStore` function from our package
99

1010
## Example {docsify-ignore}
1111
```typescript
12-
import { configureStore, IModuleStore } from "redux-dynamic-modules";
12+
import { createStore, IModuleStore } from "redux-dynamic-modules";
1313
import {getUsersModule} from "./usersModule";
1414

15-
const store: IModuleStore<IState> = configureStore(
15+
const store: IModuleStore<IState> = createStore(
1616
/* initial state */
1717
{},
1818

docs/reference/ReduxObservable.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ You can use `redux-dynamic-modules` alongside `redux-observable` so that you can
33

44
To use
55
* `npm install redux-dynamic-modules-observable`
6-
* Add the observable extension to the `configureStore` call
6+
* Add the observable extension to the `createStore` call
77

88
```typescript
9-
import { configureStore, IModuleStore } from "redux-dynamic-modules";
9+
import { createStore, IModuleStore } from "redux-dynamic-modules";
1010
import { getObservableExtension } from "redux-dynamic-modules-observable";
1111
import { getUsersModule } from "./usersModule";
1212

13-
const store: IModuleStore<IState> = configureStore(
13+
const store: IModuleStore<IState> = createStore(
1414
/* initial state */
1515
{},
1616

docs/reference/ReduxSaga.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ You can use `redux-dynamic-modules` alongside `redux-saga` so that you can add/r
33

44
To use
55
* `npm install redux-dynamic-modules-saga`
6-
* Add the saga extension to the `configureStore` call
6+
* Add the saga extension to the `createStore` call
77

88
```typescript
9-
import { configureStore, IModuleStore } from "redux-dynamic-modules";
9+
import { createStore, IModuleStore } from "redux-dynamic-modules";
1010
import { getSagaExtension } from "redux-dynamic-modules-saga";
1111
import { getUsersModule } from "./usersModule";
1212

13-
const store: IModuleStore<IState> = configureStore(
13+
const store: IModuleStore<IState> = createStore(
1414
/* initial state */
1515
{},
1616

docs/reference/ReduxThunk.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ You can use `redux-dynamic-modules` alongside `redux-thunk`.
33

44
To use
55
* `npm install redux-dynamic-modules-thunk`
6-
* Add the thunk extension to the `configureStore` call
6+
* Add the thunk extension to the `createStore` call
77

88
```typescript
9-
import { configureStore, IModuleStore } from "redux-dynamic-modules";
9+
import { createStore, IModuleStore } from "redux-dynamic-modules";
1010
import { getThunkExtension } from "redux-dynamic-modules-thunk";
1111
import { getUsersModule } from "./usersModule";
1212

13-
const store: IModuleStore<IState> = configureStore(
13+
const store: IModuleStore<IState> = createStore(
1414
/* initial state */
1515
{},
1616

packages/redux-dynamic-modules-saga/src/__tests__/SagaExtension.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { configureStore } from "redux-dynamic-modules";
1+
import { createStore } from "redux-dynamic-modules";
22
import { getSagaExtension } from "../SagaExtension";
33
import { ISagaModule } from "../Contracts";
44
import { SagaIterator } from "redux-saga";
55
describe("Saga extension tests", () => {
66
it("Saga extension registers module and starts saga", () => {
77
const testContext = {};
88
called = false;
9-
configureStore({}, [getSagaExtension(testContext)], getTestModule());
9+
createStore({}, [getSagaExtension(testContext)], getTestModule());
1010
expect(called);
1111

1212
expect(testContext["moduleManager"]).toBeTruthy();

packages/redux-dynamic-modules/src/Contracts.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ export interface IModuleManager {
5151
* Add the given module to the store
5252
*/
5353
addModule: (module: IModule<any>) => IDynamicallyAddedModule
54+
/**
55+
* Adds the given set of modules to the store
56+
*/
5457
addModules: (modules: IModule<any>[]) => IDynamicallyAddedModule;
5558
}
5659

packages/redux-dynamic-modules/src/Managers/ModuleManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export function getModuleManager<State>(middlewareManager: IItemManager<Middlewa
6060
_reducerManager.remove(key);
6161
}
6262
}
63-
// Create reduce function which redirects to _reduers.reduce
63+
// Create reduce function which redirects to _reducers.reduce
6464
const _reduce = (s: State, a: AnyAction) => {
6565
if (_reducerManager) {
6666
return _reducerManager.reduce(s, a);

packages/redux-dynamic-modules/src/Managers/ReducerManager.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ export function getReducerManager<S extends {}>(
6363
}
6464
keysToRemove = [];
6565
}
66+
67+
if (state === undefined) {
68+
state = {} as S;
69+
}
70+
6671
return combinedReducer(state, action);
6772
};
6873

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import {
2+
applyMiddleware,
3+
DeepPartial,
4+
StoreEnhancer,
5+
StoreCreator,
6+
Reducer,
7+
Action,
8+
compose as composeEnhancers
9+
} from "redux";
10+
import { IModule, IExtension, IModuleStore } from "./Contracts";
11+
import { getModuleManager } from "./Managers/ModuleManager";
12+
import { getRefCountedManager } from "./Managers/RefCountedManager";
13+
import { getMiddlewareManager } from './Managers/MiddlewareManager';
14+
15+
/**
16+
* Adds dynamic module management capabilities to a redux store.
17+
* @param extensions: Optional. Any extensions for the store e.g. to support redux-saga or redux-thunk
18+
* @param initialModules: Optional. Any modules to bootstrap the store with
19+
*/
20+
export function moduleEnhancer<S1>(
21+
extensions: IExtension[] = [],
22+
initialModules: IModule<S1>[] = []): StoreEnhancer<IModuleStore<S1>, S1> {
23+
24+
return (createStore: StoreCreator) =>
25+
<S, A extends Action, Ext>(
26+
baseReducer?: Reducer<S, A>,
27+
preloadedState?: DeepPartial<S>,
28+
baseEnhancer?: StoreEnhancer<Ext>) => {
29+
30+
// get middlewares from extensions if any
31+
const extensionMiddlewares = extensions.reduce(
32+
(mw, p) => {
33+
if (p.middleware) {
34+
mw.push(...p.middleware)
35+
}
36+
return mw;
37+
},
38+
[]
39+
);
40+
41+
// create manager to manage dynamic middlewares
42+
const middlewareManager = getRefCountedManager(
43+
getMiddlewareManager(),
44+
(a, b) => a === b);
45+
46+
// create module manager
47+
const moduleManager = getRefCountedManager(
48+
getModuleManager<any>(
49+
middlewareManager,
50+
extensions),
51+
(a: IModule<any>, b: IModule<any>) => a.id === b.id);
52+
53+
// Create module enhancer to manage extensions and dynamic middlewares
54+
const moduleEnhancer = composeEnhancers(
55+
applyMiddleware(...extensionMiddlewares,
56+
middlewareManager.enhancer));
57+
58+
// compose the moduleEnahancer with the enhancers passed, the order matters as we want to be additive
59+
// so the right parameter is the enhancer we received
60+
const composedEnhancer =
61+
(baseEnhancer ?
62+
composeEnhancers(moduleEnhancer, baseEnhancer) :
63+
moduleEnhancer);
64+
65+
// build a chained reducer
66+
const chainedReducer = (state, action) => {
67+
// call the passed in reducer first
68+
const intermediateState = baseReducer ? baseReducer(state, action) : state;
69+
// then delegate to the module managers reducer
70+
return moduleManager.getReducer(intermediateState, action);
71+
};
72+
73+
// Create the store
74+
const store = createStore(chainedReducer, preloadedState, composedEnhancer);
75+
76+
// module manager will use the dispatch from store to dispatch initial and final actions
77+
moduleManager.setDispatch(store.dispatch);
78+
79+
// adds given modules to mouldManager
80+
const addModules = (modulesToBeAdded: IModule<any>[]) => {
81+
moduleManager.add(modulesToBeAdded);
82+
return {
83+
remove: () => {
84+
moduleManager.remove(modulesToBeAdded);
85+
}
86+
};
87+
}
88+
89+
// Adds the module to the module manager
90+
const addModule = (moduleToBeAdded: IModule<any>) => {
91+
return addModules([moduleToBeAdded]);
92+
};
93+
94+
95+
const dispose = () => {
96+
// get all added modules and remove them
97+
moduleManager.dispose();
98+
middlewareManager.dispose();
99+
extensions.forEach(p => {
100+
if (p.dispose) {
101+
p.dispose();
102+
}
103+
});
104+
};
105+
106+
// Allow extensions to react when modules are added
107+
extensions.forEach(p => {
108+
if (p.onModuleManagerCreated) {
109+
p.onModuleManagerCreated({
110+
addModule,
111+
addModules
112+
});
113+
}
114+
});
115+
116+
const moduleStore = {
117+
addModule,
118+
addModules,
119+
dispose
120+
}
121+
122+
// Add the initial modules
123+
moduleStore.addModules(initialModules);
124+
return {
125+
...store as any,
126+
...moduleStore
127+
};
128+
}
129+
}

0 commit comments

Comments
 (0)