Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the ModuleStore to use ModuleEnhancer #25

Merged
1 commit merged into from
Nov 5, 2018
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,10 @@ export function getUsersModule(): IModule<IUserState> {
* Create a `ModuleStore`

```typescript
import {configureStore, IModuleStore} from "redux-dynamic-modules";
import {createStore, IModuleStore} from "redux-dynamic-modules";
This conversation was marked as resolved.
Show resolved Hide resolved
import {getUsersModule} from "./usersModule";

const store: IModuleStore<IState> = configureStore(
const store: IModuleStore<IState> = createStore(
/* initial state */
{},

Expand Down
4 changes: 2 additions & 2 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export function getUsersModule(): IModule<IUserState> {
* Create a `ModuleStore`

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

const store: IModuleStore<IState> = configureStore(
const store: IModuleStore<IState> = createStore(
/* initial state */
{},

Expand Down
4 changes: 2 additions & 2 deletions docs/reference/Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ export interface IExtension {
```

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


Expand Down
6 changes: 3 additions & 3 deletions docs/reference/ModuleStore.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ The **Module Store** is a Redux store with the added capability of managing **Re
* `addModules(modules: IModule<any>[])`: Same as `addModule`, but for multiple modules. The return function will remove all the added modules.
* `dispose()`: Remove all of the modules added to the store and dispose of the object

To create a `ModuleStore`, use the `configureStore` function from our package
To create a `ModuleStore`, use the `createStore` function from our package

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

const store: IModuleStore<IState> = configureStore(
const store: IModuleStore<IState> = createStore(
/* initial state */
{},

Expand Down
6 changes: 3 additions & 3 deletions docs/reference/ReduxObservable.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ You can use `redux-dynamic-modules` alongside `redux-observable` so that you can

To use
* `npm install redux-dynamic-modules-observable`
* Add the observable extension to the `configureStore` call
* Add the observable extension to the `createStore` call

```typescript
import { configureStore, IModuleStore } from "redux-dynamic-modules";
import { createStore, IModuleStore } from "redux-dynamic-modules";
import { getObservableExtension } from "redux-dynamic-modules-observable";
import { getUsersModule } from "./usersModule";

const store: IModuleStore<IState> = configureStore(
const store: IModuleStore<IState> = createStore(
/* initial state */
{},

Expand Down
6 changes: 3 additions & 3 deletions docs/reference/ReduxSaga.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ You can use `redux-dynamic-modules` alongside `redux-saga` so that you can add/r

To use
* `npm install redux-dynamic-modules-saga`
* Add the saga extension to the `configureStore` call
* Add the saga extension to the `createStore` call

```typescript
import { configureStore, IModuleStore } from "redux-dynamic-modules";
import { createStore, IModuleStore } from "redux-dynamic-modules";
import { getSagaExtension } from "redux-dynamic-modules-saga";
import { getUsersModule } from "./usersModule";

const store: IModuleStore<IState> = configureStore(
const store: IModuleStore<IState> = createStore(
/* initial state */
{},

Expand Down
6 changes: 3 additions & 3 deletions docs/reference/ReduxThunk.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ You can use `redux-dynamic-modules` alongside `redux-thunk`.

To use
* `npm install redux-dynamic-modules-thunk`
* Add the thunk extension to the `configureStore` call
* Add the thunk extension to the `createStore` call

```typescript
import { configureStore, IModuleStore } from "redux-dynamic-modules";
import { createStore, IModuleStore } from "redux-dynamic-modules";
import { getThunkExtension } from "redux-dynamic-modules-thunk";
import { getUsersModule } from "./usersModule";

const store: IModuleStore<IState> = configureStore(
const store: IModuleStore<IState> = createStore(
/* initial state */
{},

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { configureStore } from "redux-dynamic-modules";
import { createStore } from "redux-dynamic-modules";
import { getSagaExtension } from "../SagaExtension";
import { ISagaModule } from "../Contracts";
import { SagaIterator } from "redux-saga";
describe("Saga extension tests", () => {
it("Saga extension registers module and starts saga", () => {
const testContext = {};
called = false;
configureStore({}, [getSagaExtension(testContext)], getTestModule());
createStore({}, [getSagaExtension(testContext)], getTestModule());
expect(called);

expect(testContext["moduleManager"]).toBeTruthy();
Expand Down
3 changes: 3 additions & 0 deletions packages/redux-dynamic-modules/src/Contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export interface IModuleManager {
* Add the given module to the store
*/
addModule: (module: IModule<any>) => IDynamicallyAddedModule
/**
* Adds the given set of modules to the store
*/
addModules: (modules: IModule<any>[]) => IDynamicallyAddedModule;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export function getModuleManager<State>(middlewareManager: IItemManager<Middlewa
_reducerManager.remove(key);
}
}
// Create reduce function which redirects to _reduers.reduce
// Create reduce function which redirects to _reducers.reduce
const _reduce = (s: State, a: AnyAction) => {
if (_reducerManager) {
return _reducerManager.reduce(s, a);
Expand Down
5 changes: 5 additions & 0 deletions packages/redux-dynamic-modules/src/Managers/ReducerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ export function getReducerManager<S extends {}>(
}
keysToRemove = [];
}

if (state === undefined) {
state = {} as S;
}

return combinedReducer(state, action);
};

Expand Down
129 changes: 129 additions & 0 deletions packages/redux-dynamic-modules/src/ModuleEnhancer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import {
applyMiddleware,
DeepPartial,
StoreEnhancer,
StoreCreator,
Reducer,
Action,
compose as composeEnhancers
} from "redux";
import { IModule, IExtension, IModuleStore } from "./Contracts";
import { getModuleManager } from "./Managers/ModuleManager";
import { getRefCountedManager } from "./Managers/RefCountedManager";
import { getMiddlewareManager } from './Managers/MiddlewareManager';

/**
* Adds dynamic module management capabilities to a redux store.
* @param extensions: Optional. Any extensions for the store e.g. to support redux-saga or redux-thunk
* @param initialModules: Optional. Any modules to bootstrap the store with
*/
export function moduleEnhancer<S1>(
extensions: IExtension[] = [],
initialModules: IModule<S1>[] = []): StoreEnhancer<IModuleStore<S1>, S1> {

return (createStore: StoreCreator) =>
<S, A extends Action, Ext>(
baseReducer?: Reducer<S, A>,
preloadedState?: DeepPartial<S>,
baseEnhancer?: StoreEnhancer<Ext>) => {

// get middlewares from extensions if any
const extensionMiddlewares = extensions.reduce(
(mw, p) => {
if (p.middleware) {
mw.push(...p.middleware)
}
return mw;
},
[]
);

// create manager to manage dynamic middlewares
const middlewareManager = getRefCountedManager(
getMiddlewareManager(),
(a, b) => a === b);

// create module manager
const moduleManager = getRefCountedManager(
getModuleManager<any>(
middlewareManager,
extensions),
(a: IModule<any>, b: IModule<any>) => a.id === b.id);

// Create module enhancer to manage extensions and dynamic middlewares
const moduleEnhancer = composeEnhancers(
applyMiddleware(...extensionMiddlewares,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: newlines

middlewareManager.enhancer));

// compose the moduleEnahancer with the enhancers passed, the order matters as we want to be additive
// so the right parameter is the enhancer we received
const composedEnhancer =
(baseEnhancer ?
composeEnhancers(moduleEnhancer, baseEnhancer) :
moduleEnhancer);

// build a chained reducer
const chainedReducer = (state, action) => {
// call the passed in reducer first
const intermediateState = baseReducer ? baseReducer(state, action) : state;
// then delegate to the module managers reducer
return moduleManager.getReducer(intermediateState, action);
};

// Create the store
const store = createStore(chainedReducer, preloadedState, composedEnhancer);

// module manager will use the dispatch from store to dispatch initial and final actions
moduleManager.setDispatch(store.dispatch);

// adds given modules to mouldManager
This conversation was marked as resolved.
Show resolved Hide resolved
const addModules = (modulesToBeAdded: IModule<any>[]) => {
moduleManager.add(modulesToBeAdded);
return {
remove: () => {
moduleManager.remove(modulesToBeAdded);
}
};
}

// Adds the module to the module manager
const addModule = (moduleToBeAdded: IModule<any>) => {
return addModules([moduleToBeAdded]);
};


const dispose = () => {
// get all added modules and remove them
moduleManager.dispose();
middlewareManager.dispose();
extensions.forEach(p => {
if (p.dispose) {
p.dispose();
}
});
};

// Allow extensions to react when modules are added
extensions.forEach(p => {
if (p.onModuleManagerCreated) {
p.onModuleManagerCreated({
addModule,
addModules
});
}
});

const moduleStore = {
addModule,
addModules,
dispose
}

// Add the initial modules
moduleStore.addModules(initialModules);
return {
...store as any,
...moduleStore
};
}
}
Loading