Skip to content

Commit 2fc5d2b

Browse files
authored
feat(almin-react-container): Can pass props to <Container {...props} /> (#294)
* feat(almin-react-container): Can pass props to <Container {...props} /> * fix(almin-react-container): fix example
1 parent 792ef4a commit 2fc5d2b

File tree

8 files changed

+123
-47
lines changed

8 files changed

+123
-47
lines changed

packages/almin-react-container/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ const RootContainer = AlminReactContainer.create(App, context);
6666
ReactDOM.render(<RootContainer />, document.getElementById("js-app"));
6767
```
6868

69-
See [Example/](./example/)
69+
For more details, see [Example/](./example/).
70+
71+
For TypeScript user, see [almin-react-container-test.tsx](./test/almin-react-container-test.tsx).
7072

7173
## Changelog
7274

packages/almin-react-container/example/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
"version": "1.0.0",
1515
"main": "lib/example.js",
1616
"dependencies": {
17-
"almin": "^0.12.0-3",
17+
"almin": "^0.14.0",
1818
"almin-react-container": "file:../",
19-
"react": "^15.4.2",
20-
"react-dom": "^15.4.2"
19+
"prop-types": "^15.6.0",
20+
"react": "^16.1.0",
21+
"react-dom": "^16.1.0"
2122
},
2223
"devDependencies": {
2324
"babel-cli": "^6.24.0",

packages/almin-react-container/example/src/index.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22
"use strict";
33
import React from "react";
44
import ReactDOM from "react-dom";
5+
import PropTypes from "prop-types";
56
import { Dispatcher, Context, Store } from "almin";
6-
import AlminReactContainer from "almin-react-container";
7+
import { AlminReactContainer } from "almin-react-container";
8+
79
// Store
810
class MyState {
911
constructor({ value }) {
1012
this.value = value;
1113
}
1214
}
15+
1316
class MyStore extends Store {
1417
constructor() {
1518
super();
@@ -24,19 +27,22 @@ class MyStore extends Store {
2427
};
2528
}
2629
}
30+
2731
// Context
2832
const context = new Context({
2933
dispatcher: new Dispatcher(),
3034
store: new MyStore()
3135
});
36+
3237
// View
3338
class App extends React.Component {
3439
render() {
3540
return <div>{this.props.myState.value}</div>;
3641
}
3742
}
43+
3844
App.propTypes = {
39-
myState: React.PropTypes.instanceOf(MyState).isRequired
45+
myState: PropTypes.instanceOf(MyState).isRequired
4046
};
4147
// Create Container
4248
const RootContainer = AlminReactContainer.create(App, context);

packages/almin-react-container/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@
3939
},
4040
"devDependencies": {
4141
"@types/node": "^7.0.12",
42-
"@types/react": "^15.0.20",
42+
"@types/react": "16.0.22",
43+
"@types/react-dom": "16.0.3",
4344
"almin": "^0.14.0",
4445
"babel-preset-env": "^1.4.0",
4546
"babel-preset-power-assert": "^1.0.0",
@@ -51,7 +52,7 @@
5152
"power-assert": "^1.4.2",
5253
"react": "^15.4.2",
5354
"react-dom": "^15.5.4",
54-
"typescript": "^2.3.2"
55+
"typescript": "^2.6.1"
5556
},
5657
"dependencies": {
5758
"shallow-equal-object": "^1.0.1"

packages/almin-react-container/src/almin-react-container.tsx

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,22 @@ import * as assert from "assert";
44
import { Context } from "almin";
55
import { shallowEqual } from "shallow-equal-object";
66

7-
export default class AlminReactContainer {
8-
static create<T>(
7+
// Diff typing utilities
8+
// https://qiita.com/cotttpan/items/999fe07d079298c35e0c
9+
export type AlminReactContainerDiffKey<T extends string, U extends string> = ({ [P in T]: P } &
10+
{ [P in U]: never } & { [x: string]: never })[T];
11+
12+
export type AlminReactContainerOmit<T, K extends keyof T> = Pick<T, AlminReactContainerDiffKey<keyof T, K>>;
13+
14+
export type AlminReactContainerDiff<T, U> = AlminReactContainerOmit<T, keyof U & keyof T>;
15+
16+
export type AlminReactContainerWeakDiff<T, U> = AlminReactContainerDiff<T, U> & { [K in (keyof U & keyof T)]?: T[K] };
17+
18+
export class AlminReactContainer {
19+
static create<T, P>(
920
WrappedComponent: React.ComponentClass<T>,
10-
context: Context<any>
11-
): React.ComponentClass<{} | void> {
21+
context: Context<P>
22+
): React.ComponentClass<AlminReactContainerWeakDiff<T, P>> {
1223
if (process.env.NODE_ENV !== "production") {
1324
assert.ok(
1425
typeof WrappedComponent === "function",
@@ -17,14 +28,16 @@ export default class AlminReactContainer {
1728
assert.ok(context instanceof Context, "`context` should be instance of Almin's Context");
1829
}
1930
const componentName = WrappedComponent.displayName || WrappedComponent.name;
20-
return class AlminContainer extends React.Component<T, {}> {
31+
// AlminContainer's props is <T - P> type
32+
// T is State of Store, P is custom props of the `WrappedComponent`
33+
return class AlminContainer extends React.Component<AlminReactContainerWeakDiff<T, P>> {
2134
static displayName = `AlminContainer(${componentName})`;
2235

23-
state: T;
36+
state: P;
2437
unSubscribe: () => void | null;
2538

26-
constructor() {
27-
super();
39+
constructor(props: any) {
40+
super(props);
2841
this.state = context.getState();
2942
}
3043

@@ -49,8 +62,10 @@ export default class AlminReactContainer {
4962

5063
render() {
5164
// Workaround TS2.3.1: https://github.com/Microsoft/TypeScript/pull/13288
52-
return <WrappedComponent {...this.state as any} />;
65+
return <WrappedComponent {...this.state as any} {...this.props} />;
5366
}
5467
};
5568
}
5669
}
70+
71+
export default AlminReactContainer;

packages/almin-react-container/src/test/almin-react-container-test.tsx

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// It is test file for d.ts
33
import AlminReactContainer from "../almin-react-container";
44
import * as React from "react";
5-
import { Dispatcher, Context, Store } from "almin";
5+
import * as ReactDOM from "react-dom";
6+
import { Dispatcher, Context, Store, StoreGroup } from "almin";
7+
68
// Store
79
class MyState {
810
value: string;
@@ -11,7 +13,8 @@ class MyState {
1113
this.value = value;
1214
}
1315
}
14-
class MyStore extends Store {
16+
17+
class MyStore extends Store<MyState> {
1518
state: MyState;
1619

1720
constructor() {
@@ -22,26 +25,39 @@ class MyStore extends Store {
2225
}
2326

2427
getState() {
25-
return {
26-
myState: this.state
27-
};
28+
return this.state;
2829
}
2930
}
31+
32+
const storeGroup = new StoreGroup({
33+
myState: new MyStore()
34+
});
3035
// Context
3136
const context = new Context({
3237
dispatcher: new Dispatcher(),
33-
store: new MyStore()
38+
store: storeGroup
3439
});
3540

3641
// View
42+
43+
type AppState = typeof storeGroup.state & {
44+
// defined by App View
45+
custom?: string;
46+
};
47+
3748
class App extends React.Component<AppState, {}> {
3849
render() {
3950
return <div>{this.props.myState.value}</div>;
4051
}
4152
}
42-
interface AppState {
43-
myState: MyState;
44-
}
53+
4554
// Create Container
46-
const RootContainer = AlminReactContainer.create<AppState>(App, context);
55+
const RootContainer = AlminReactContainer.create(App, context);
4756
console.log(RootContainer);
57+
58+
ReactDOM.render(<RootContainer />, document.body);
59+
// custom is optional
60+
React.createElement(RootContainer);
61+
React.createElement(RootContainer, {
62+
custom: "value"
63+
});

packages/almin-react-container/test/almin-react-container-test.js

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,24 @@ const createTestStore = initialState => {
1212
}
1313

1414
updateState(newState) {
15-
this.state = newState;
16-
this.emitChange();
15+
if (this.state !== newState) {
16+
this.state = newState;
17+
this.emitChange();
18+
}
1719
}
1820

1921
getState() {
2022
return this.state;
2123
}
2224
}
25+
2326
return new TestStore();
2427
};
2528
describe("almin-react-container", () => {
2629
context("when update with new state", () => {
2730
it("should update WrapperComponent with new props", () => {
2831
let updatedCount = 0;
32+
2933
class Passthrough extends Component {
3034
componentWillUpdate() {
3135
updatedCount++;
@@ -35,6 +39,7 @@ describe("almin-react-container", () => {
3539
return <div />;
3640
}
3741
}
42+
3843
// initial state
3944
const initialState = {
4045
testKey: "initial value"
@@ -65,6 +70,7 @@ describe("almin-react-container", () => {
6570
context("when update with same state", () => {
6671
it("should not update WrapperComponent", () => {
6772
let updatedCount = 0;
73+
6874
class Passthrough extends Component {
6975
componentWillUpdate() {
7076
updatedCount++;
@@ -74,6 +80,7 @@ describe("almin-react-container", () => {
7480
return <div />;
7581
}
7682
}
83+
7784
// initial state
7885
const initialState = {
7986
testKey: "initial value"
@@ -99,4 +106,33 @@ describe("almin-react-container", () => {
99106
assert.deepEqual(stub.props, newState, "should not update props");
100107
});
101108
});
109+
context("with custom props", () => {
110+
it("should pass props to Container", () => {
111+
let updatedCount = 0;
112+
113+
class Passthrough extends Component {
114+
componentWillUpdate() {
115+
updatedCount++;
116+
}
117+
118+
render() {
119+
return <div />;
120+
}
121+
}
122+
123+
// initial state
124+
const initialState = {
125+
testKey: "initial value"
126+
};
127+
const testStore = createTestStore(initialState);
128+
const context = new Context({
129+
dispatcher: new Dispatcher(),
130+
store: testStore
131+
});
132+
const Container = AlminReactContainer.create(Passthrough, context);
133+
const tree = TestUtils.renderIntoDocument(<Container custom="value" />);
134+
const stub = TestUtils.findRenderedComponentWithType(tree, Passthrough);
135+
assert.equal(stub.props.custom, "value", "should have custom value");
136+
});
137+
});
102138
});

packages/almin-react-container/yarn.lock

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,24 @@
22
# yarn lockfile v1
33

44

5+
"@types/node@*":
6+
version "8.0.51"
7+
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.51.tgz#b31d716fb8d58eeb95c068a039b9b6292817d5fb"
8+
59
"@types/node@^7.0.12":
610
version "7.0.12"
711
resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9"
812

9-
"@types/react@^15.0.20":
10-
version "15.0.20"
11-
resolved "https://registry.yarnpkg.com/@types/react/-/react-15.0.20.tgz#3d4a05ed04a8b1847ac6c8fa6093591d3910fba5"
13+
"@types/react-dom@16.0.3":
14+
version "16.0.3"
15+
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.0.3.tgz#8accad7eabdab4cca3e1a56f5ccb57de2da0ff64"
16+
dependencies:
17+
"@types/node" "*"
18+
"@types/react" "*"
19+
20+
"@types/react@*", "@types/react@16.0.22":
21+
version "16.0.22"
22+
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.0.22.tgz#19ad106e124aceebd2b4d430a278d55413ee8759"
1223

1324
abab@^1.0.3:
1425
version "1.0.3"
@@ -35,14 +46,6 @@ ajv@^4.9.1:
3546
co "^4.6.0"
3647
json-stable-stringify "^1.0.1"
3748

38-
almin@^0.12.0:
39-
version "0.12.0"
40-
resolved "https://registry.yarnpkg.com/almin/-/almin-0.12.0.tgz#814dc41ee4acc0e8c46a20473414079f954f3e12"
41-
dependencies:
42-
map-like "^1.1.2"
43-
object-assign "^4.1.0"
44-
shallow-equal-object "^1.0.1"
45-
4649
amdefine@>=0.0.4:
4750
version "1.0.1"
4851
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -1240,10 +1243,6 @@ lru-cache@^4.0.1:
12401243
pseudomap "^1.0.1"
12411244
yallist "^2.0.0"
12421245

1243-
map-like@^1.1.2:
1244-
version "1.1.2"
1245-
resolved "https://registry.yarnpkg.com/map-like/-/map-like-1.1.2.tgz#f42a301ebd9290372b9e4ddc49e5df9804a507b9"
1246-
12471246
mime-db@~1.27.0:
12481247
version "1.27.0"
12491248
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
@@ -1720,9 +1719,9 @@ type-name@^2.0.1:
17201719
version "2.0.2"
17211720
resolved "https://registry.yarnpkg.com/type-name/-/type-name-2.0.2.tgz#efe7d4123d8ac52afff7f40c7e4dec5266008fb4"
17221721

1723-
typescript@^2.3.2:
1724-
version "2.3.2"
1725-
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.2.tgz#f0f045e196f69a72f06b25fd3bd39d01c3ce9984"
1722+
typescript@^2.6.1:
1723+
version "2.6.1"
1724+
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.1.tgz#ef39cdea27abac0b500242d6726ab90e0c846631"
17261725

17271726
ua-parser-js@^0.7.9:
17281727
version "0.7.12"

0 commit comments

Comments
 (0)