Skip to content
This repository was archived by the owner on Oct 19, 2019. It is now read-only.

Commit 07c262f

Browse files
committed
feat: support model mode
1 parent b3ba77c commit 07c262f

4 files changed

Lines changed: 184 additions & 60 deletions

File tree

package.json

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,10 @@
3535
},
3636
"homepage": "https://talkingdata.github.io/rxloop-loading",
3737
"peerDependencies": {
38-
"@rxloop/core": ">= 0.10.0",
3938
"rxjs": "^6.0.0-0"
4039
},
4140
"devDependencies": {
42-
"@rxloop/core": "^0.10.0",
41+
"@rxloop/core": "^0.11.0",
4342
"babel-cli": "^6.26.0",
4443
"babel-core": "^6.26.2",
4544
"babel-jest": "^22.4.3",
@@ -70,5 +69,8 @@
7069
"es",
7170
"src",
7271
"index.d.ts"
73-
]
72+
],
73+
"dependencies": {
74+
"immer": "^1.7.2"
75+
}
7476
}

src/index.js

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import produce from "immer";
2+
13
export default function loading(
24
config = {
35
name: 'loading',
46
}
57
) {
68
return function init({
7-
onModel$,
9+
onModelBeforeCreate$,
810
onEpicStart$,
911
onEpicEnd$,
1012
onEpicCancel$,
@@ -14,40 +16,71 @@ export default function loading(
1416
const _model = {
1517
name: config.name,
1618
state: {
17-
global: 0,
1819
epics: {},
1920
},
2021
reducers: {
2122
init(state, action) {
22-
state.epics = action.epics;
23-
24-
return state;
23+
return produce(state, draft => {
24+
draft.epics = action.epics;
25+
});
2526
},
2627
epicStart(state, action) {
27-
const epicCounterKey = `${action.epic}Counter`;
28-
let epicCounter = state.epics[action.model][epicCounterKey] + action.loading;
29-
30-
state.epics[action.model][epicCounterKey] = epicCounter;
31-
state.epics[action.model][action.epic] = epicCounter > 0;
28+
return produce(state, draft => {
29+
const epicCounterKey = `${action.epic}Counter`;
30+
let epicCounter = draft.epics[action.model][epicCounterKey] + action.loading;
3231

33-
return state;
32+
draft.epics[action.model][epicCounterKey] = epicCounter;
33+
draft.epics[action.model][action.epic] = epicCounter > 0;
34+
});
3435
},
3536
epicStop(state, action) {
36-
const epicCounterKey = `${action.epic}Counter`;
37-
38-
state.epics[action.model][epicCounterKey] = 0;
39-
state.epics[action.model][action.epic] = false;
40-
41-
return state;
37+
return produce(state, draft => {
38+
const epicCounterKey = `${action.epic}Counter`;
39+
draft.epics[action.model][epicCounterKey] = 0;
40+
draft.epics[action.model][action.epic] = false;
41+
});
4242
},
4343
},
4444
};
4545
this.model(_model);
4646
this.stream('loading').subscribe();
47+
48+
onModelBeforeCreate$.subscribe(({ model }) => {
49+
if (
50+
typeof model.state !== 'object' ||
51+
!model.epics ||
52+
model.state.loading !== void 0
53+
) return;
54+
55+
const loading = {};
56+
Object.keys(model.epics).forEach(epic => {
57+
loading[`${epic}Counter`] = 0;
58+
loading[epic] = false;
59+
});
60+
61+
model.state.loading = loading;
62+
model.reducers.loadingStart = loadingStart;
63+
model.reducers.loadingEnd = loadingEnd;
64+
65+
function loadingStart(state, { payload: { epic } }) {
66+
return produce(state, draft => {
67+
const epicCounterKey = `${epic}Counter`;
68+
const epicCounter = draft.loading[epicCounterKey] + 1
69+
draft.loading[epicCounterKey] = epicCounter;
70+
draft.loading[epic] = epicCounter > 0;
71+
});
72+
}
73+
74+
function loadingEnd(state, { payload: { epic } }) {
75+
return produce(state, draft => {
76+
draft.loading[`${epic}Counter`] = 0;
77+
draft.loading[epic] = false;
78+
});
79+
}
80+
});
4781

4882
// hooks
49-
onStart$
50-
.subscribe(() => {
83+
onStart$.subscribe(() => {
5184
const epics = {};
5285
Object.keys(this._stream).forEach((model) => {
5386
if (model === 'loading') return;
@@ -63,47 +96,59 @@ export default function loading(
6396
});
6497
});
6598

66-
onEpicStart$
67-
.subscribe(data => {
99+
onEpicStart$.subscribe(({ model, epic }) => {
68100
this.dispatch({
69-
epic: data.epic,
101+
model,
102+
epic,
70103
type: 'loading/epicStart',
71-
model: data.model,
72104
loading: 1,
73105
});
106+
this.dispatch({
107+
type: `${model}/loadingStart`,
108+
payload: { epic },
109+
});
74110
});
75111

76-
onEpicEnd$
77-
.subscribe(data => {
112+
onEpicEnd$.subscribe(({ model, epic }) => {
78113
this.dispatch({
79-
epic: data.epic,
114+
model,
115+
epic,
80116
type: 'loading/epicStop',
81-
model: data.model,
82117
loading: 0,
83118
isEnd: true,
84119
});
120+
this.dispatch({
121+
type: `${model}/loadingEnd`,
122+
payload: { epic },
123+
});
85124
});
86125

87-
onEpicError$
88-
.subscribe(data => {
126+
onEpicError$.subscribe(({ model, epic }) => {
89127
this.dispatch({
90-
epic: data.epic,
128+
model,
129+
epic,
91130
type: 'loading/epicStop',
92-
model: data.model,
93131
loading: 0,
94132
isError: true,
95133
});
134+
this.dispatch({
135+
type: `${model}/loadingEnd`,
136+
payload: { epic },
137+
});
96138
});
97139

98-
onEpicCancel$
99-
.subscribe(data => {
140+
onEpicCancel$.subscribe(({ model, epic }) => {
100141
this.dispatch({
101-
epic: data.epic,
142+
model,
143+
epic,
102144
type: 'loading/epicStop',
103-
model: data.model,
104145
loading: 0,
105146
isCancel: true,
106147
});
148+
this.dispatch({
149+
type: `${model}/loadingEnd`,
150+
payload: { epic },
151+
});
107152
});
108153
};
109154
};

test/index.spec.js

Lines changed: 92 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ app.model({
1515
},
1616
reducers: {
1717
add(state) {
18-
return state;
18+
return {
19+
...state,
20+
a: state.a + 1,
21+
};
1922
},
2023
},
2124
epics: {
@@ -44,7 +47,7 @@ app.model({
4447
},
4548
});
4649

47-
// app.stream('loading').subscribe(data => console.log(data));
50+
app.stream('test').subscribe();
4851

4952
app.start();
5053

@@ -55,10 +58,18 @@ describe('test epic loading', () => {
5558
});
5659

5760
test('loading state', () => {
58-
const state = app.getState('loading');
59-
delete state.__action__;
60-
expect(state).toEqual({
61-
global: 0,
61+
expect(app.getState('test')).toEqual({
62+
a: 1,
63+
loading: {
64+
getData: false,
65+
getDataCounter: 0,
66+
setData: false,
67+
setDataCounter: 0,
68+
getSlowlyData: false,
69+
getSlowlyDataCounter: 0,
70+
},
71+
});
72+
expect(app.getState('loading')).toEqual({
6273
epics: {
6374
test: {
6475
getData: false,
@@ -76,10 +87,18 @@ describe('test epic loading', () => {
7687
app.dispatch({
7788
type: 'test/getSlowlyData',
7889
});
79-
const state = app.getState('loading');
80-
delete state.__action__;
90+
expect(app.getState('test')).toEqual({
91+
a: 1,
92+
loading: {
93+
getData: false,
94+
getDataCounter: 0,
95+
setData: false,
96+
setDataCounter: 0,
97+
getSlowlyData: true,
98+
getSlowlyDataCounter: 1,
99+
},
100+
});
81101
expect(app.getState('loading')).toEqual({
82-
global: 0,
83102
epics: {
84103
test: {
85104
getData: false,
@@ -92,10 +111,18 @@ describe('test epic loading', () => {
92111
},
93112
});
94113
setTimeout(() => {
95-
const state = app.getState('loading');
96-
delete state.__action__;
97-
expect(state).toEqual({
98-
global: 0,
114+
expect(app.getState('test')).toEqual({
115+
a: 2,
116+
loading: {
117+
getData: false,
118+
getDataCounter: 0,
119+
setData: false,
120+
setDataCounter: 0,
121+
getSlowlyData: false,
122+
getSlowlyDataCounter: 0,
123+
},
124+
});
125+
expect(app.getState('loading')).toEqual({
99126
epics: {
100127
test: {
101128
getData: false,
@@ -137,16 +164,19 @@ describe('test epic loading when error', () => {
137164
});
138165

139166
app.start();
140-
141167
app.stream('loading').subscribe();
142-
168+
let state = null;
169+
app.stream('test').subscribe(
170+
data => (state = data),
171+
() => {},
172+
);
143173
test('loading test', (done) => {
144174
app.dispatch({ type: 'test/getDataError' });
145175
setTimeout(() => {
146-
const state = app.getState('loading');
147-
delete state.__action__;
148-
expect(state).toEqual({
149-
global: 0,
176+
expect(state).toEqual(
177+
{ loading: { getDataErrorCounter: 0, getDataError: false } }
178+
);
179+
expect(app.getState('loading')).toEqual({
150180
epics: {
151181
test: {
152182
getDataError: false,
@@ -157,4 +187,47 @@ describe('test epic loading when error', () => {
157187
done();
158188
}, 3000);
159189
});
190+
});
191+
192+
describe('test epic loading when error', () => {
193+
const app = rxloop({
194+
plugins: [ loading() ],
195+
});
196+
app.model({
197+
name: 'test',
198+
state: {
199+
loading: true,
200+
},
201+
reducers: {
202+
add(state) {
203+
return state;
204+
}
205+
},
206+
epics: {
207+
getData(action$) {
208+
return action$.pipe(
209+
mapTo({
210+
type: 'add',
211+
}),
212+
);
213+
},
214+
},
215+
});
216+
217+
app.start();
218+
app.stream('loading').subscribe();
219+
220+
test('Should not to replace loading state', () => {
221+
expect(app.getState('test')).toEqual({
222+
loading: true,
223+
});
224+
expect(app.getState('loading')).toEqual({
225+
epics: {
226+
test: {
227+
getDataCounter: 0,
228+
getData: false,
229+
}
230+
}
231+
});
232+
});
160233
});

yarn.lock

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
esutils "^2.0.2"
1717
js-tokens "^4.0.0"
1818

19-
"@rxloop/core@^0.10.0":
20-
version "0.10.0"
21-
resolved "https://registry.npmjs.org/@rxloop/core/-/core-0.10.0.tgz#8ce18326f85444d692b326968756b9605dece736"
19+
"@rxloop/core@^0.11.0":
20+
version "0.11.0"
21+
resolved "https://registry.npmjs.org/@rxloop/core/-/core-0.11.0.tgz#077daaf289dd5a0044dbf2314faf31a0b6f8b94f"
2222
dependencies:
2323
invariant "^2.2.4"
2424
is-plain-object "^2.0.4"
@@ -1917,6 +1917,10 @@ ignore-walk@^3.0.1:
19171917
dependencies:
19181918
minimatch "^3.0.4"
19191919

1920+
immer@^1.7.2:
1921+
version "1.7.2"
1922+
resolved "https://registry.npmjs.org/immer/-/immer-1.7.2.tgz#a51e9723c50b27e132f6566facbec1c85fc69547"
1923+
19201924
import-local@^1.0.0:
19211925
version "1.0.0"
19221926
resolved "https://registry.npmjs.org/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"

0 commit comments

Comments
 (0)