Skip to content

Commit 23ed6e3

Browse files
committed
prepare createReduce
#89
1 parent 5c3d546 commit 23ed6e3

File tree

12 files changed

+336
-225
lines changed

12 files changed

+336
-225
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { expect } from "chai";
2+
import sinon from "sinon";
3+
import createReducer from "../createReducer";
4+
5+
describe("Test createReducer", () => {
6+
let reduce;
7+
beforeEach(() => {
8+
reduce = createReducer((state, action) => action, []);
9+
});
10+
11+
it("basic test", () => {
12+
const [store, dispatch] = reduce;
13+
const action = { aaa: "bbb" };
14+
dispatch(action);
15+
expect(store.getState()).to.deep.equal(action);
16+
});
17+
18+
it("dispatch empty", () => {
19+
const [store, dispatch] = reduce;
20+
dispatch();
21+
expect(store.getState()).to.be.empty;
22+
});
23+
24+
it("Emit with custom event", (done) => {
25+
const [store, dispatch] = reduce;
26+
const callback = sinon.spy();
27+
store.addListener(callback);
28+
dispatch();
29+
setTimeout(() => {
30+
expect(callback.called).to.be.true;
31+
done();
32+
});
33+
});
34+
35+
it("could support text dispatch", () => {
36+
const [store, dispatch] = reduce;
37+
dispatch("xxx");
38+
expect(store.getState()).to.deep.equal({ type: "xxx" });
39+
});
40+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import callfunc from "call-func";
2+
import { UNDEFINED, T_UNDEFINED } from "reshow-constant";
3+
4+
const emitter = () => {
5+
const pool = [];
6+
return {
7+
add: (handler) => pool.push(handler),
8+
remove: (handler) => pool.splice(pool.indexOf(handler) >>> 0, 1),
9+
emit: (state) =>
10+
setTimeout(() => {
11+
for (let i = 0, j = pool.length; i < j; i++) {
12+
pool[i](state);
13+
}
14+
}),
15+
};
16+
};
17+
18+
const createReducer = (reduce, initState = {}) => {
19+
const state = { current: callfunc(initState) };
20+
const mitt = emitter();
21+
const dispatch = (action = {}, params) => {
22+
if (action.trim) {
23+
action = { type: action };
24+
params && (action.params = params);
25+
}
26+
const startingState = state.current;
27+
const endingState = reduce(startingState, action);
28+
if (endingState === T_UNDEFINED) {
29+
console.error(`reduce() return ${UNDEFINED}.`);
30+
}
31+
if (startingState !== endingState) {
32+
state.current = endingState;
33+
mitt.emit(state);
34+
}
35+
};
36+
const store = {
37+
getState: () => state.current,
38+
addListener: mitt.add,
39+
removeListener: mitt.remove,
40+
};
41+
return [store, dispatch];
42+
};
43+
44+
export default createReducer;
+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export { default as createReducer } from "./createReducer";
12
export { default as Store, CHANGE } from "./Store";
23
export { default as Dispatcher } from "./Dispatcher";

packages/reshow-flux-base/yarn.lock

+155-155
Large diffs are not rendered by default.

packages/reshow-flux/compile.sh

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ stop(){
1515
}
1616

1717
watch(){
18-
stop
19-
npm run build:cjs -- --watch &
20-
npm run build:es -- --watch &
18+
stop
19+
npm run build:cjs -- --watch &
20+
npm run build:es -- --watch &
2121
}
2222

2323
case "$1" in
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Map, Set, fromJS } from "immutable";
2+
import { toJS } from "get-object-value";
3+
import { createReducer } from "reshow-flux-base";
4+
5+
const ImmutableStore = (reduce) => createReducer(reduce, Map());
6+
7+
const getMap = (state, k) => toJS(state.get(k)) || {};
8+
9+
const mergeMap = (state, JSArray) => state.merge(fromJS(JSArray));
10+
11+
export default ImmutableStore;
12+
export {
13+
Map,
14+
Set,
15+
getMap,
16+
mergeMap
17+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { expect } from "chai";
2+
3+
import ImmutableStore, { Map, mergeMap, getMap } from "../ImmutableStore";
4+
5+
describe("Test ImmutableStore", () => {
6+
const reducee = (state, action) => mergeMap(state, action);
7+
8+
it("test merge map", () => {
9+
const [store, dispatch] = ImmutableStore(reducee);
10+
const action = { aaa: { bbb: "ccc" } };
11+
dispatch(action);
12+
const state = store.getState();
13+
expect(Map.isMap(state.get("aaa"))).to.be.true;
14+
expect(state.get("aaa").toJS()).to.deep.equal({ bbb: "ccc" });
15+
});
16+
17+
it("test get map", () => {
18+
const [store, dispatch] = ImmutableStore(reducee);
19+
const action = { aaa: { bbb: "ccc" } };
20+
dispatch(action);
21+
const state = store.getState();
22+
expect(getMap(state, 'aaa')).to.deep.equal({ bbb: "ccc" });
23+
});
24+
});

packages/reshow-flux/src/__tests__/connectHookMoreTest.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,15 @@ describe("Test Connect hook for more test", () => {
6363
};
6464

6565
expect(calculateTimes).to.equal(0);
66-
const html = mount(<FakeComponent />);
66+
const wrap = mount(<FakeComponent />);
6767
setTimeout(() => {
6868
expect(calculateTimes).to.equal(2); //init and handlchange
6969
dispatch({ aaa: "Hello dispatcher!" });
7070
setTimeout(() => {
71-
html.update();
71+
wrap.update();
7272
expect(calculateTimes).to.equal(3);
73-
expect(html.html()).to.equal("<div>Hello dispatcher!</div>");
73+
expect(wrap.html()).to.equal("<div>Hello dispatcher!</div>");
74+
wrap.unmount();
7475
dispatch({ aaa: "Hello Unmount!" });
7576
expect(calculateTimes).to.equal(3);
7677
done();

packages/reshow-flux/src/__tests__/connectHookTest.js

+4-16
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,18 @@
11
import React, { Component } from "react";
2-
32
import { expect } from "chai";
43
import { mount } from "reshow-unit";
4+
import { createReducer } from "reshow-flux-base";
55

66
import useConnect from "../useConnect";
7-
import { Dispatcher, ReduceStore } from "../index";
87

98
describe("Test Connect Hook", () => {
10-
class FakeStore extends ReduceStore {
11-
getInitialState() {
12-
return {};
13-
}
14-
15-
reduce(state, action) {
16-
return action;
17-
}
18-
}
19-
20-
let dispatcher;
219
let dispatch;
2210
let store;
2311

2412
beforeEach(() => {
25-
dispatcher = new Dispatcher();
26-
dispatch = dispatcher.dispatch;
27-
store = new FakeStore(dispatcher);
13+
const reducer = createReducer((state, action) => action, []);
14+
store = reducer[0];
15+
dispatch = reducer[1];
2816
});
2917

3018
it("basic test", (done) => {

packages/reshow-flux/src/getStores.js

-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import callfunc from "call-func";
22
import { IS_ARRAY } from "reshow-constant";
3-
import dedup from "array.dedup";
4-
import { removeEmpty } from "array.merge";
53

64
const storeLocator = (props) => props?.stores;
75

@@ -11,7 +9,6 @@ const getStores = ({ props, options }) => {
119
if (!IS_ARRAY(stores)) {
1210
stores = [stores];
1311
}
14-
stores = dedup(removeEmpty(stores));
1512
const firstStore = stores[0];
1613
return { stores, allProps, firstStore };
1714
};
+43-45
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import "setimmediate";
22
import { useState, useEffect, useDebugValue } from "react";
3-
import { CHANGE } from "reshow-flux-base";
43
import { useMounted } from "reshow-hooks";
54
import { T_TRUE, T_FALSE } from "reshow-constant";
5+
import { CHANGE } from "reshow-flux-base";
66

77
import getStores from "./getStores";
88

@@ -26,53 +26,51 @@ const handleShouldComponentUpdate = ({
2626
};
2727
};
2828

29-
const useConnect = (options) => (props) => {
30-
const {
31-
calculateState,
32-
shouldComponentUpdate,
33-
displayName = "useConnect",
34-
} = options || {};
35-
useDebugValue(displayName);
36-
const [data, setData] = useState(() => ({
37-
props,
38-
state: calculateState({}, props, options),
39-
}));
29+
const useConnect =
30+
(options = {}) =>
31+
(props) => {
32+
const {
33+
calculateState,
34+
shouldComponentUpdate,
35+
displayName = "useConnect",
36+
immutable,
37+
} = options;
38+
useDebugValue(displayName);
39+
const [data, setData] = useState(() => ({
40+
props,
41+
state: calculateState({}, props, options),
42+
}));
4043

41-
const isMount = useMounted();
44+
const isMount = useMounted();
4245

43-
useEffect(() => {
44-
const { stores } = getStores({ options, props });
45-
const storeLen = stores.length;
46-
if (storeLen) {
47-
const handleChange = () => {
48-
if (T_FALSE !== isMount()) {
49-
setData((prev) =>
50-
handleShouldComponentUpdate({
51-
options,
52-
shouldComponentUpdate,
53-
calculateState,
54-
prev,
55-
props,
56-
})
57-
);
46+
useEffect(
47+
() => {
48+
const { allProps, firstStore } = getStores({ options, props });
49+
const handleChange = () => {
50+
if (T_FALSE !== isMount()) {
51+
setData((prev) =>
52+
handleShouldComponentUpdate({
53+
options,
54+
shouldComponentUpdate,
55+
calculateState,
56+
prev,
57+
props,
58+
})
59+
);
60+
}
61+
};
62+
if (!data.__init__ || data.props !== props) {
63+
handleChange();
5864
}
59-
};
60-
if (!data.__init__ || data.props !== props) {
61-
handleChange();
62-
}
63-
const asyncHandleChange = () => setImmediate(handleChange);
64-
for (let i = 0; i < storeLen; i++) {
65-
stores[i].addListener(asyncHandleChange, CHANGE);
66-
}
67-
return () => {
68-
stores.forEach((store) =>
69-
store?.removeListener(asyncHandleChange, CHANGE)
70-
);
71-
};
72-
}
73-
}, [props]);
65+
firstStore.addListener(handleChange, CHANGE);
66+
return () => {
67+
firstStore.removeListener(handleChange, CHANGE);
68+
};
69+
},
70+
immutable ? [] : [props]
71+
);
7472

75-
return data.state || {};
76-
};
73+
return data.state || {};
74+
};
7775

7876
export default useConnect;

packages/reshow-runtime/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"private": true,
23
"name": "reshow-runtime",
34
"repository": "react-atomic/reshow",
45
"keywords": [

0 commit comments

Comments
 (0)