Skip to content

Commit 72548da

Browse files
committed
Fixes and tests
1 parent 647fdff commit 72548da

File tree

8 files changed

+368
-19
lines changed

8 files changed

+368
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
node_modules
22
dist
3+
coverage
34
*.lock
45
*.log

.travis.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
language: node_js
2+
branches:
3+
only:
4+
- master
5+
cache:
6+
directories:
7+
- node_modules
8+
install:
9+
- npm install
10+
script:
11+
- npm test
12+
- npm run coverage

package.json

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
],
1313
"scripts": {
1414
"build": "tsc -p .",
15-
"test": "exit 0"
15+
"test": "./node_modules/.bin/jest --collectCoverage",
16+
"test:watch": "./node_modules/.bin/jest --watch",
17+
"coverage": "./node_modules/.bin/codecov"
1618
},
1719
"keywords": [
1820
"react",
@@ -22,9 +24,43 @@
2224
],
2325
"license": "MIT",
2426
"dependencies": {},
27+
"peerDependencies": {
28+
"react": ">=16.3.0"
29+
},
2530
"devDependencies": {
31+
"@types/enzyme": "^3.9.0",
32+
"@types/enzyme-adapter-react-16": "^1.0.5",
33+
"@types/jest": "^24.0.11",
2634
"@types/react": "^16.8.8",
35+
"codecov": "^3.2.0",
36+
"enzyme": "^3.9.0",
37+
"enzyme-adapter-react-16": "^1.11.2",
38+
"enzyme-to-json": "^3.3.5",
39+
"jest": "^24.5.0",
40+
"raf": "^3.4.1",
2741
"react": "^16.8.4",
42+
"react-dom": "^16.8.4",
43+
"ts-jest": "^24.0.0",
2844
"typescript": "^3.3.3333"
45+
},
46+
"jest": {
47+
"moduleFileExtensions": [
48+
"ts",
49+
"tsx",
50+
"js"
51+
],
52+
"transform": {
53+
"\\.(ts|tsx)$": "ts-jest"
54+
},
55+
"setupFiles": [
56+
"raf/polyfill"
57+
],
58+
"testRegex": "/tests/.*\\.(ts|tsx|js)$",
59+
"setupFilesAfterEnv": [
60+
"<rootDir>/setupTests.ts"
61+
],
62+
"snapshotSerializers": [
63+
"enzyme-to-json"
64+
]
2965
}
3066
}

setupTests.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import * as Enzyme from 'enzyme'
2+
import * as Adapter from 'enzyme-adapter-react-16'
3+
4+
Enzyme.configure({
5+
adapter: new Adapter(),
6+
})

src/helperTypes.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

src/index.tsx

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,50 @@
11
import * as React from 'react'
2-
import { Params, DropFirst } from 'src/helperTypes'
32

4-
type Action<TState, TArgs extends any[]= any, TReturn = void> =
5-
(setState: React.Dispatch<React.SetStateAction<TState>>, ...args: TArgs) => TReturn
3+
/*
4+
Custom Helper types
5+
*/
6+
7+
// Custom Params helper type because existing counterpart "Parameters" uses any instead of never, which is faulsy
8+
export type Params<T extends (...args: never[]) => unknown> = T extends (...args: infer P) => any ? P : never;
9+
10+
// Custom RetType helper type because existing counterpart "ReturnType" uses any instead of never, which is faulsy
11+
export type RetType<T extends (...args: never[]) => unknown> = T extends (...args: never[]) => infer R ? R : any;
12+
13+
// For [First, ...Rest] tuple gets [Rest] tuple
14+
export type DropFirst<T extends any[]> =
15+
((...args: T) => any) extends (arg: any, ...rest: infer U) => any[] ? U : T;
16+
17+
export type Tail<T> = T extends Array<any>
18+
? ((...args: T) => never) extends ((a: any, ...args: infer R) => never)
19+
? R
20+
: never
21+
: never
22+
23+
/*
24+
API Types
25+
*/
26+
27+
type ContextReference<TState> = {
28+
setState: React.Dispatch<React.SetStateAction<TState>>
29+
useContext: <T>(context: React.Context<T>) => T
30+
}
31+
32+
type Action<TState, TArgs extends never[]= never[], TReturn = any> =
33+
(contextReference: ContextReference<TState>, ...args: TArgs) => TReturn
634

735
type Actions<TState> = { [key: string]: Action<TState> }
836

937
type MappedActions<TState, TActions extends Actions<TState>> = {
10-
[P in keyof TActions]: (...args: DropFirst<Params<TActions[P]>>) => ReturnType<TActions[P]>
38+
[P in keyof TActions]: (...args: DropFirst<Params<TActions[P]>>) => RetType<TActions[P]>
1139
}
1240

1341
type Store<TState, TActions extends Actions<TState>> =
1442
TActions extends undefined ? TState : TState & MappedActions<TState, TActions>
1543

44+
/*
45+
Logic
46+
*/
47+
1648
export default function createStateContext<TState, TActions extends Actions<TState> = {}>(
1749
initialState: TState,
1850
actions?: TActions
@@ -22,8 +54,11 @@ export default function createStateContext<TState, TActions extends Actions<TSta
2254

2355
const provider: React.FC = props => {
2456
let [_state, setState] = React.useState(initialState)
25-
26-
const _actions = mapActionsToDispatch(setState, actions)
57+
const useContext = React.useContext
58+
const _actions = mapActionsToDispatch({
59+
setState,
60+
useContext
61+
}, actions)
2762

2863
const _store = { ..._state, ..._actions } as Store<TState, TActions>
2964

@@ -38,15 +73,16 @@ export default function createStateContext<TState, TActions extends Actions<TSta
3873
}
3974

4075
function mapActionsToDispatch<TState, TActions extends Actions<TState>>(
41-
dispatch: React.Dispatch<React.SetStateAction<TState>>,
76+
contextReference: ContextReference<TState>,
4277
actions?: TActions,
4378
): MappedActions<TState, TActions> {
4479
if (actions === undefined) return {} as MappedActions<TState, TActions>
4580
return Object.keys(actions).reduce(
4681
(obj, key) => {
82+
const action = actions[key]
4783
return {
4884
...obj,
49-
[key]: (...payload: never[]) => actions[key](dispatch, payload[0])
85+
[key]: (...args: never[]) => actions[key](contextReference, ...args)
5086
}
5187
},
5288
{} as MappedActions<TState, TActions>)
@@ -60,8 +96,7 @@ function mapActionsToDefault<TState, TActions extends Actions<TState>>(
6096
(obj, key) => {
6197
return {
6298
...obj,
63-
[key]: (...payload: never[]) =>
64-
() => { throw new Error(`Can't invoke ${key} because provider does not exist`) }
99+
[key]: (...args: never[]) => { throw new Error(`Can't invoke ${key} because provider does not exist`) }
65100
}
66101
},
67102
{} as MappedActions<TState, TActions>)

0 commit comments

Comments
 (0)