Skip to content

Commit c537741

Browse files
author
Péter Hauszknecht
committed
refact middlewares
1 parent bde7cea commit c537741

3 files changed

Lines changed: 42 additions & 44 deletions

File tree

src/store/index.ts

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,31 @@ export default class Store<State, R = Reducer<State>> implements IStore<State, R
1919
getState: GetState<State> = () => this.state;
2020

2121
dispatch: Dispatch<R> = reducer => {
22-
assertReducer(reducer);
23-
const finalReducer = this.applyMiddlewares(reducer);
24-
if (typeof finalReducer === 'function') {
25-
this.state = finalReducer(this.state);
26-
this.listeners.forEach(listener => listener());
27-
}
28-
return <R>finalReducer;
22+
if (typeof reducer !== 'function')
23+
throw new Error('Reducer is not a function: dispatch takes only reducers as functions.');
24+
this.state = reducer(this.state);
25+
this.listeners.forEach(listener => listener());
26+
return <R>reducer;
2927
};
3028

3129
subscribe = (listener: Listener): Unsubscribe => {
32-
assertListener(listener);
30+
if (typeof listener !== 'function')
31+
throw new Error('Listener is not a function: subscribe takes only listeners as functions.');
3332
this.listeners = [ ...this.listeners, listener ];
3433
return () => (this.listeners = this.listeners.filter(lis => lis !== listener));
3534
};
3635

37-
addMiddleware = <R2>(...middlewares: Middleware<State>[]): Store<State, R | R2> => {
38-
assertMiddlewares(middlewares);
39-
this.middlewares = [ ...this.middlewares, ...middlewares ];
36+
addMiddleware = <R2>(...middlewares: Middleware<State, R, R2>[]): Store<State, R | R2> => {
37+
if (middlewares.some(middleware => typeof middleware !== 'function'))
38+
throw new Error('Middleware is not a function: addMiddleware takes only middlewares as functions.');
39+
middlewares.forEach(middleware => {
40+
const prevDispatch = this.dispatch;
41+
const dispatch = reducer => middleware(this)(prevDispatch)(reducer);
42+
this.dispatch = dispatch;
43+
});
4044
return this;
4145
};
4246

4347
private applyMiddlewares = (reducer: R): R =>
4448
<R>this.middlewares.reduce((prevReducer, middleware) => middleware(this, prevReducer), reducer);
4549
}
46-
47-
function assertReducer(reducer) {
48-
if (typeof reducer !== 'function')
49-
throw new Error('Reducer is not a function: dispatch takes only reducers as functions.');
50-
}
51-
52-
function assertListener(listener) {
53-
if (typeof listener !== 'function')
54-
throw new Error('Listener is not a function: subscribe takes only listeners as functions.');
55-
}
56-
57-
function assertMiddlewares(middlewares) {
58-
if (middlewares.some(middleware => typeof middleware !== 'function')) {
59-
throw new Error('Middleware is not a function: addMiddleware takes only middlewares as functions.');
60-
}
61-
}

src/store/middlewares/thunk.ts

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,35 @@
11
import { Middleware, GetState, Dispatch, Reducer, Store } from '../types';
22

3-
export interface ThunkMiddleware<State, ExtraArgument> extends Middleware<State> {
4-
withExtraArgument: (extraArgument: ExtraArgument) => ThunkMiddleware<State, ExtraArgument>;
3+
export interface ThunkMiddleware<State, ExtraArgument>
4+
extends Middleware<State, Reducer<State>, Thunk<State, ExtraArgument, any>> {
5+
withExtraArgument: <EA>(extraArgument: EA) => ThunkMiddleware<State, EA>;
56
}
67

7-
export interface Delegate<State, ExtraArgument, T> {
8-
(dispatch: ThunkDispatch<State, ExtraArgument>, getState: GetState<State>, extraArgument: ExtraArgument): T;
8+
export interface Delegate<State, ExtraArgument, Return> {
9+
(dispatch: ThunkDispatch<State, ExtraArgument>, getState: GetState<State>, extraArgument: ExtraArgument): Return;
910
}
1011

1112
export interface ThunkDispatch<State, ExtraArgument> extends Dispatch<Reducer<State>> {
12-
<T>(reducer: Thunk<State, ExtraArgument, T>): T;
13+
<Return>(reducer: Thunk<State, ExtraArgument, Return>): Return;
1314
}
1415

15-
export interface Thunk<State, ExtraArgument, T> {
16-
(state: State): Delegate<State, ExtraArgument, T>;
16+
export interface Thunk<State, ExtraArgument, Return> {
17+
(state: State): Delegate<State, ExtraArgument, Return>;
1718
}
1819

19-
function thunkFactory<T>(extraArgument?: T): ThunkMiddleware<any, T> {
20-
const thunk = ((store, reducer) => {
20+
const thunkFactory = (extraArgument?) => {
21+
const thunk = store => next => reducer => {
22+
if (typeof reducer !== 'function') throw new Error('Thunk requires reducers as functions');
2123
const state = store.getState();
2224
const result = reducer(state);
23-
return typeof result === 'function' ? result(store.dispatch, store.getState, extraArgument) : reducer;
24-
}) as ThunkMiddleware<any, T>;
25-
thunk.withExtraArgument = thunkFactory;
25+
if (typeof result === 'function') return result(store.dispatch, store.getState, extraArgument);
26+
else {
27+
next(_ => result);
28+
return reducer;
29+
}
30+
};
31+
thunk['withExtraArgument'] = thunkFactory;
2632
return thunk;
27-
}
33+
};
2834

29-
export default thunkFactory();
35+
export default thunkFactory() as ThunkMiddleware<any, any>;

src/store/types.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@ export interface Unsubscribe {
1818
(): void;
1919
}
2020

21-
export interface Middleware<State> {
22-
(store: Store<State>, reducer: Reducer<State>): Reducer<State>;
21+
export interface Middleware<State, R1, R2> {
22+
(store: Store<State, R1>): {
23+
(next: Dispatch<R1>): {
24+
(reducer: R2): any;
25+
}
26+
};
2327
}
2428

2529
export interface Store<State, R = Reducer<State>> {
2630
getState: GetState<State>;
2731
dispatch: Dispatch<R>;
2832
subscribe(listener: Listener): Unsubscribe;
29-
addMiddleware<R2>(...middlewares: Middleware<State>[]): Store<State, R | R2>;
33+
addMiddleware<R2>(...middlewares: Middleware<State, R, R2>[]): Store<State, R | R2>;
3034
}

0 commit comments

Comments
 (0)