Skip to content
This repository was archived by the owner on Feb 14, 2023. It is now read-only.

Commit 337d943

Browse files
committed
resolves #116
1 parent 12bf981 commit 337d943

File tree

16 files changed

+1187
-984
lines changed

16 files changed

+1187
-984
lines changed

default.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import DispatchFunction from './types/dispatch-function';
12
import Dispatchers from './types/dispatchers';
3+
import NewGlobalState from './types/new-global-state';
24

3-
export type Dispatch = Dispatchers<State, Reducers>;
5+
export type Dispatch = DispatchFunction<State> & Dispatchers<State, Reducers>;
46

57
export interface Reducers { }
68

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "reactn",
3-
"version": "2.1.7",
3+
"version": "2.2.0",
44
"author": "Charles Stover <[email protected]>",
55
"description": "React, but with built-in global state management.",
66
"homepage": "https://github.com/CharlesStover/reactn#readme",
@@ -36,7 +36,7 @@
3636
"@babel/preset-env": "^7.4.2",
3737
"@babel/preset-typescript": "^7.3.3",
3838
"@testing-library/jest-dom": "^4.0.0",
39-
"@testing-library/react": "^8.0.5",
39+
"@testing-library/react": "8.0.5",
4040
"@types/jest": "^24.0.11",
4141
"@types/node": "^12.6.2",
4242
"@types/react": "^16.7.13",
@@ -46,8 +46,8 @@
4646
"eslint": "^5.15.2",
4747
"eslint-plugin-react": "^7.12.4",
4848
"jest": "^24.6.0",
49-
"react": "^16.8.6",
50-
"react-dom": "^16.8.6",
49+
"react": "~16.8",
50+
"react-dom": "~16.8",
5151
"ts-jest": "^24.0.1",
5252
"ts-node": "^8.0.2",
5353
"typescript": "^3.3.1"

src/global-state-manager.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Reducers, State } from '../default';
22
import Callback from '../types/callback';
3+
import DispatchFunction from '../types/dispatch-function';
34
import Dispatcher, { ExtractArguments } from '../types/dispatcher';
45
import Dispatchers from '../types/dispatchers';
56
import Middleware, { MiddlewareCreator } from '../types/middleware';
@@ -135,14 +136,25 @@ export default class GlobalStateManager<
135136
): Dispatcher<G, A> {
136137
return (...args: A): Promise<G> => {
137138
return this.set(
138-
reducer(this.state, this.dispatchers, ...args),
139-
name,
139+
reducer(this.state, this.dispatcherMap, ...args),
140+
name,
140141
args,
141142
)
142143
.then((): G => this.state);
143144
};
144145
}
145146

147+
// The dispatcher map, contrary to this.dispatchers, is the map that belongs
148+
// *to* a dispatcher. In addition to the map *of* dispatchers, it includes
149+
// a dispatch function that sets the global state.
150+
public get dispatcherMap(): DispatchFunction<G> & Dispatchers<G, R> {
151+
const dispatch: DispatchFunction<G> = (newGlobalState: NewGlobalState<G>): Promise<G> =>
152+
this.set(newGlobalState).then((): G => this.state);
153+
return Object.assign(dispatch, this.dispatchers);
154+
}
155+
156+
// The dispatchers are a map *of* dispatchers. This is in contrast to
157+
// this.dispatcherMap, which is a map that belongs *to* a dispatcher.
146158
public get dispatchers(): Dispatchers<G, R> {
147159
return copyObject(this._dispatchers);
148160
}

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
ReactNComponentClass,
55
ReactNPureComponentClass,
66
} from '../types/component-class';
7+
import DispatchFunction from '../types/dispatch-function';
78
import Dispatcher, { ExtractArguments, PropertyDispatcher } from '../types/dispatcher';
89
import Dispatchers from '../types/dispatchers';
910
import NewGlobalState from '../types/new-global-state';
@@ -93,7 +94,7 @@ interface ReactN extends Omit<typeof React, 'Component' | 'default' | 'PureCompo
9394

9495
// useDispatch()
9596
useDispatch<G extends {} = State, R extends {} = Reducers>(
96-
): Dispatchers<G, R>;
97+
): DispatchFunction<G> & Dispatchers<G, R>;
9798

9899
// useDispatch(Function)
99100
useDispatch<G extends {} = State, R extends {} = Reducers, A extends any[] = any[]>(

src/set-global.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export default function _setGlobal<
3030
globalStateManager,
3131
callback(
3232
globalStateManager.state,
33-
globalStateManager.dispatchers,
33+
globalStateManager.dispatcherMap,
3434
stateChange,
3535
),
3636
)

src/use-dispatch.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useContext } from 'react';
22
import useForceUpdate from 'use-force-update';
33
import { Reducers, State } from '../default';
4+
import DispatchFunction from '../types/dispatch-function';
45
import Dispatcher, {
56
ExtractArguments,
67
PropertyDispatcher,
@@ -37,7 +38,7 @@ export default function _useDispatch<
3738
R extends {} = Reducers,
3839
>(
3940
overrideGlobalStateManager: GlobalStateManager<G, R> | null,
40-
): Dispatchers<G, R>;
41+
): DispatchFunction<G> & Dispatchers<G, R>;
4142

4243
// useDispatch(Function)
4344
export default function _useDispatch<
@@ -113,7 +114,7 @@ export default function _useDispatch<
113114

114115
// Return all dispatchers.
115116
if (typeof reducer === 'undefined') {
116-
return globalStateManager.dispatchers;
117+
return globalStateManager.dispatcherMap;
117118
}
118119

119120
// Use a custom reducer.

tests/context/use-dispatch-undefined.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ describe('Context useDispatch()', (): void => {
7878
});
7979

8080

81-
it('should return an object', (): void => {
82-
expect(typeof dispatchers).toBe('object');
81+
it('should return a function', (): void => {
82+
expect(dispatchers).toBeInstanceOf(Function);
8383
});
8484

8585
it('should return the global dispatch object', async (): Promise<void> => {

tests/global-state-manager/create-dispatcher.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,10 @@ describe('GlobalStateManager.createDispatcher', (): void => {
7171
expect(spy).toHaveBeenCalledTimes(1);
7272
expect(spy).toHaveBeenCalledWith(
7373
startState,
74-
globalStateManager.dispatchers,
74+
expect.anything(),
7575
...REDUCER_ARGS,
7676
);
77+
expect(spy.mock.calls[0][1].toString()).toBe(globalStateManager.dispatcherMap.toString());
7778
});
7879

7980
const spy = spyOn('set');
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import GlobalStateManager from '../../src/global-state-manager';
2+
import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
3+
4+
const STATE_CHANGE: Partial<G> = {
5+
x: true,
6+
};
7+
8+
const NEW_STATE: G = {
9+
...INITIAL_STATE,
10+
...STATE_CHANGE,
11+
};
12+
13+
describe('GlobalStateManager.dispatcherMap', (): void => {
14+
15+
let globalStateManager: GlobalStateManager<G, R>;
16+
beforeEach((): void => {
17+
globalStateManager = new GlobalStateManager<G, R>(INITIAL_STATE, INITIAL_REDUCERS);
18+
});
19+
20+
21+
22+
it('should be a function with 1 argument', (): void => {
23+
expect(globalStateManager.dispatcherMap).toBeInstanceOf(Function);
24+
expect(globalStateManager.dispatcherMap).toHaveLength(1);
25+
});
26+
27+
it('should update and return the global state', async (): Promise<void> => {
28+
expect(globalStateManager.state).not.toEqual(NEW_STATE);
29+
const newGlobalState: G = await globalStateManager.dispatcherMap(STATE_CHANGE);
30+
expect(newGlobalState).toEqual(NEW_STATE);
31+
expect(globalStateManager.state).toEqual(NEW_STATE);
32+
});
33+
34+
it('should have a property for each reducer', (): void => {
35+
for (const reducer of Object.keys(INITIAL_REDUCERS)) {
36+
expect(globalStateManager.dispatcherMap).toHaveProperty(reducer);
37+
expect(globalStateManager.dispatcherMap[reducer]).toBeInstanceOf(Function);
38+
}
39+
});
40+
});

tests/provider/set-global.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import createProvider from '../../src/create-provider';
2+
import DispatchFunction from '../../types/dispatch-function';
3+
import Dispatchers from '../../types/dispatchers';
24
import ReactNProvider from '../../types/provider';
35
import { G, INITIAL_REDUCERS, INITIAL_STATE, R } from '../utils/initial';
46
import { hasContext } from '../utils/react-version';
@@ -43,11 +45,12 @@ describe('Provider.setGlobal', (): void => {
4345
it(
4446
'should execute a callback with the new state',
4547
async (): Promise<void> => {
46-
const CALLBACK: jest.Mock<void, []> = jest.fn();
48+
const CALLBACK: jest.Mock<void, [ G, DispatchFunction<G> & Dispatchers<G, R>]> = jest.fn();
4749
await Provider.setGlobal(STATE_CHANGE, CALLBACK);
4850
expect(CALLBACK).toHaveBeenCalledTimes(1);
4951
expect(CALLBACK)
50-
.toHaveBeenCalledWith(NEW_STATE, Provider.dispatch, STATE_CHANGE);
52+
.toHaveBeenCalledWith(NEW_STATE, expect.anything(), STATE_CHANGE);
53+
// expect(CALLBACK.mock.calls[0][1].toString()).toBe(Provider.dispatcherMap.toString());
5154
}
5255
);
5356

0 commit comments

Comments
 (0)