Skip to content

Commit ebf1573

Browse files
author
Sergio Daniel Xalambrí
committed
v1.0.0
1 parent 4917031 commit ebf1573

File tree

8 files changed

+307
-1
lines changed

8 files changed

+307
-1
lines changed

.babelrc

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"plugins": [
3+
"transform-es2015-modules-commonjs",
4+
"transform-es2015-arrow-functions",
5+
"transform-es2015-parameters",
6+
"transform-es2015-block-scoping",
7+
"transform-es2015-computed-properties",
8+
"transform-object-rest-spread",
9+
"transform-es2015-shorthand-properties"
10+
]
11+
}

.eslintrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "airbnb",
3+
"parser": "babel-eslint"
4+
}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,5 @@ node_modules
3131

3232
# Optional REPL history
3333
.node_repl_history
34+
35+
build

.npmignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
node_modules
2+
lib
3+
test
4+
logs
5+
*.log
6+
.babelrc
7+
.eslintrc

README.md

+55-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,56 @@
11
# redux-duck
2-
Helper function to create Redux modules easy using `ducks-modular-redux` proposal
2+
Helper function to create Redux modules using the `ducks-modular-redux` proposal.
3+
4+
## Installation
5+
```bash
6+
npm i -S redux-duck
7+
```
8+
9+
## API
10+
### Create duck
11+
```javascript
12+
import { createDuck } from 'redux-duck';
13+
14+
const myDuck = createDuck('duck-name', 'application-name');
15+
```
16+
* `createDuck` receive 2 arguments, the second argument is optional.
17+
* The first argument is the duck name.
18+
* The second, and optional, argument is the application or module name.
19+
20+
### Define action types
21+
```javascript
22+
const ACTION_TYPE = myDuck.defineType('ACTION_TYPE');
23+
```
24+
* `defineType` receive just one argument.
25+
* The argument is the name of the action.
26+
* The result should be an string like `application-name/duck-name/ACTION_TYPE` or `duck-name/ACTION_TYPE` if the application or module name was not defined.
27+
28+
### Create action creators
29+
```javascript
30+
const actionType = myDuck.createAction(ACTION_TYPE);
31+
```
32+
* `createAction` receive just one argument.
33+
* This argument should be the defined action type string.
34+
* It should return a function who will receive the action payload and return a valid (FSA compilant) action object.
35+
* The action creator will receive an optional argument with the action payload.
36+
37+
### Create reducer
38+
```javascript
39+
const initialState = {
40+
list: Immutable.List(),
41+
data: Immutable.Map(),
42+
};
43+
44+
const reducer = myDuck.createReducer({
45+
[ACTION_TYPE]: (state, action) => ({
46+
...state,
47+
list: state.list.push(action.payload.id),
48+
data: state.map.set(action.payload.id+'', action.payload),
49+
}),
50+
}, initialState);
51+
```
52+
* `createReducer` receive two arguments, both required.
53+
* The first argument is an object with the possible action cases.
54+
* The second argument is the reducer initial state.
55+
* The first argument should use the previously defined *action types* as keys.
56+
* Each key in the first argument object should be a function who will receive the current state and the dispatched action as arguments and return the updated state.

lib/index.js

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
export function createDuck(name, app) {
2+
function defineType(type) {
3+
if (app) {
4+
return `${app}/${name}/${type}`;
5+
}
6+
return `${name}/${type}`;
7+
}
8+
9+
function createReducer(cases, defaultState = {}) {
10+
return function reducer(state = defaultState, action = {}) {
11+
if (state === undefined) return defaultState;
12+
for (const caseName in cases) {
13+
if (action.type === caseName) return cases[caseName](state, action);
14+
}
15+
return state;
16+
};
17+
}
18+
19+
function createAction(type) {
20+
return function actionCreator(payload) {
21+
const action = {
22+
type,
23+
};
24+
25+
if (payload) action.payload = payload;
26+
27+
return action;
28+
};
29+
}
30+
31+
return {
32+
defineType,
33+
createReducer,
34+
createAction,
35+
};
36+
}

package.json

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"name": "redux-duck",
3+
"version": "1.0.0",
4+
"description": "Helper function to create Redux modules using the ducks-modular-redux proposal.",
5+
"main": "build/index.js",
6+
"directories": {
7+
"test": "test"
8+
},
9+
"scripts": {
10+
"lint": "eslint lib/index.js",
11+
"prebuild": "npm run lint",
12+
"build": "babel lib --out-dir build",
13+
"pretest": "npm run build",
14+
"test": "babel-node test/index.js | tap-spec",
15+
"prepublish": "npm run test"
16+
},
17+
"repository": {
18+
"type": "git",
19+
"url": "git+https://github.com/sergiodxa/redux-duck.git"
20+
},
21+
"keywords": [
22+
"redux",
23+
"duck",
24+
"module",
25+
"helper"
26+
],
27+
"author": "Sergio Daniel Xalambrí <sergio@xalambri.com.ar> (http://sergio.xalambri.com.ar/)",
28+
"license": "MIT",
29+
"bugs": {
30+
"url": "https://github.com/sergiodxa/redux-duck/issues"
31+
},
32+
"homepage": "http://sergio.xalambri.com.ar/redux-duck/",
33+
"devDependencies": {
34+
"babel": "6.5.2",
35+
"babel-cli": "6.7.5",
36+
"babel-core": "6.7.6",
37+
"babel-eslint": "6.0.3",
38+
"babel-plugin-transform-es2015-arrow-functions": "6.5.2",
39+
"babel-plugin-transform-es2015-block-scoping": "6.7.1",
40+
"babel-plugin-transform-es2015-computed-properties": "6.6.5",
41+
"babel-plugin-transform-es2015-modules-commonjs": "6.7.4",
42+
"babel-plugin-transform-es2015-parameters": "6.7.0",
43+
"babel-plugin-transform-es2015-shorthand-properties": "6.5.0",
44+
"babel-plugin-transform-object-rest-spread": "6.6.5",
45+
"eslint": "2.8.0",
46+
"eslint-config-airbnb": "7.0.0",
47+
"eslint-plugin-jsx-a11y": "0.6.2",
48+
"eslint-plugin-react": "4.3.0",
49+
"tap-spec": "4.1.1",
50+
"tape": "4.5.1"
51+
}
52+
}

test/index.js

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import test from 'tape';
2+
import { createDuck } from '../build/index.js';
3+
4+
test('create duck', t => {
5+
t.plan(6);
6+
7+
const duck = createDuck('test', 'redux-duck');
8+
9+
t.ok(duck.defineType, 'it should had a key `defineType`');
10+
t.equals(
11+
typeof duck.defineType,
12+
'function',
13+
'it should be a function'
14+
);
15+
16+
t.ok(duck.createReducer, 'it should had a key `createReducer`');
17+
t.equals(
18+
typeof duck.createReducer,
19+
'function',
20+
'it should be a function'
21+
);
22+
23+
t.ok(duck.createAction, 'it should had a key `createAction`');
24+
t.equals(
25+
typeof duck.createAction,
26+
'function',
27+
'it should be a function'
28+
);
29+
});
30+
31+
test('define type', t => {
32+
t.plan(2);
33+
34+
const duck1 = createDuck('test1', 'redux-duck');
35+
const duck2 = createDuck('test2', 'redux-duck');
36+
37+
const type1 = duck1.defineType('TYPE');
38+
const type2 = duck2.defineType('TYPE');
39+
40+
t.equals(
41+
type1,
42+
'redux-duck/test1/TYPE',
43+
'it should be the expected action type string'
44+
);
45+
46+
t.equals(
47+
type2,
48+
'redux-duck/test2/TYPE',
49+
'it should be the expected action type string'
50+
);
51+
});
52+
53+
test('create action creator', t => {
54+
t.plan(3);
55+
56+
const duck = createDuck('test', 'redux-duck');
57+
58+
const type = duck.defineType('TYPE');
59+
60+
const createType = duck.createAction(type);
61+
62+
const testData = {
63+
id: 123,
64+
message: 'hello world',
65+
};
66+
67+
const action = createType(testData);
68+
69+
const emptyActon = createType();
70+
71+
t.equals(
72+
typeof createType,
73+
'function',
74+
'it should create a valid function'
75+
);
76+
77+
t.deepEquals(
78+
action,
79+
{
80+
type,
81+
payload: testData,
82+
},
83+
'it should create a valid action object'
84+
);
85+
86+
t.deepEquals(
87+
emptyActon,
88+
{
89+
type,
90+
},
91+
'it should be able to create an action without payload'
92+
);
93+
});
94+
95+
test('create reducer', t => {
96+
t.plan(3);
97+
98+
const duck = createDuck('test', 'redux-duck');
99+
100+
const type = duck.defineType('TYPE');
101+
102+
const reducer = duck.createReducer({
103+
[type]: (state, action) => ({
104+
...state,
105+
[action.payload.id]: action.payload,
106+
}),
107+
}, {});
108+
109+
const testData = {
110+
id: 123,
111+
message: 'hello world',
112+
};
113+
114+
const testAction = {
115+
type,
116+
payload: testData,
117+
};
118+
119+
const state = reducer({}, testAction);
120+
121+
t.equals(
122+
typeof reducer,
123+
'function',
124+
'the reducer should be a function'
125+
);
126+
127+
t.deepEquals(
128+
reducer(),
129+
{},
130+
'the reducer should be able to return the default state'
131+
);
132+
133+
t.deepEquals(
134+
state,
135+
{
136+
[testData.id]: testData,
137+
},
138+
'the reducer should work with the defined cases'
139+
);
140+
});

0 commit comments

Comments
 (0)