Skip to content

Commit d6c784a

Browse files
committed
Create a HoC as an alternative to the mixin
1 parent e7fddf4 commit d6c784a

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,8 @@
2525
"babel-eslint": "^4.1.7",
2626
"eslint": "^0.22.1",
2727
"mocha": "^2.2.5"
28+
},
29+
"peerDependencies": {
30+
"react": "^15.3.0"
2831
}
2932
}

src/HoC.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React from 'react';
2+
import {QueryResult} from './QueryResult';
3+
import {ensure} from './util';
4+
5+
const update = (component, props, state) => {
6+
const observed = component.observe(props, state);
7+
const {session, subscriptions} = component._rethinkMixinState;
8+
const subscriptionManager = session._subscriptionManager;
9+
10+
// Close subscriptions no longer subscribed to
11+
Object.keys(subscriptions).forEach(key => {
12+
if (!observed[key]) {
13+
subscriptions[key].unsubscribe();
14+
delete component.data[key];
15+
}
16+
});
17+
18+
// [Re]-subscribe to active queries
19+
Object.keys(observed).forEach(key => {
20+
const queryRequest = observed[key];
21+
const oldSubscription = subscriptions[key];
22+
const queryResult = component.data[key] || new QueryResult(queryRequest.initial, queryRequest.transform);
23+
subscriptions[key] = subscriptionManager.subscribe(component, queryRequest, queryResult);
24+
component.data[key] = queryResult;
25+
if (oldSubscription) {
26+
oldSubscription.unsubscribe();
27+
}
28+
});
29+
};
30+
31+
const unmount = component => {
32+
const {subscriptions} = component._rethinkMixinState;
33+
Object.keys(subscriptions).forEach(key => {
34+
subscriptions[key].unsubscribe();
35+
});
36+
};
37+
38+
// Mixin for RethinkDB query subscription support in React components. You'll
39+
// generally want to use DefaultHoC or PropsHoC, which use BaseHoC to
40+
// create more usable versions.
41+
//
42+
// In your component, you should define an observe(props, state) function that
43+
// returns an object mapping query names to QueryRequests. See
44+
// QueryRequest.js for the API.
45+
//
46+
// In the child component, you will have access to this.props.data, which is an
47+
// object mapping from the same query names returned in observe() to the
48+
// results of each query as an QueryResult. See QueryResult.js for the
49+
// API.
50+
//
51+
// Here is a simple example of the mixin API:
52+
// const observe = (props, state) => ({
53+
// turtles: new QueryRequest({
54+
// query: r.table('turtles'),
55+
// changes: true,
56+
// initial: [],
57+
// }),
58+
// });
59+
60+
// class App extends Component {
61+
// render() {
62+
// return <div>
63+
// {this.props.data.turtles.value().map(function(x) {
64+
// return <div key={x.id}>{x.firstName}</div>;
65+
// })};
66+
// </div>;
67+
// },
68+
// };
69+
70+
// BaseHoC(new Session())(observe)(App);
71+
72+
export const BaseHoC = sessionGetter => observe => ChildComponent => class ReactRethinkDB extends React.Component {
73+
constructor() {
74+
super();
75+
this.observe = observe;
76+
}
77+
78+
componentWillMount() {
79+
const session = sessionGetter(this);
80+
ensure(session && session._subscriptionManager,
81+
`Must define Session`);
82+
ensure(this.observe, `Must define observe()`);
83+
ensure(session._connPromise, `Must connect() before mounting react-rethinkdb`);
84+
this._rethinkMixinState = {session, subscriptions: {}};
85+
this.data = this.data || {};
86+
update(this, this.props, this.state);
87+
}
88+
89+
componentDidMount() {
90+
this._rethinkMixinState.isMounted = true;
91+
}
92+
93+
componentWillUnmount() {
94+
unmount(this);
95+
this._rethinkMixinState.isMounted = false;
96+
}
97+
98+
componentWillUpdate(nextProps, nextState) {
99+
if (nextProps !== this.props || nextState !== this.state) {
100+
update(this, nextProps, nextState);
101+
}
102+
}
103+
104+
render() {
105+
return <ChildComponent data={this.data} {...this.props} />;
106+
}
107+
};
108+
109+
// HoC that uses rethink session from props. For example:
110+
// class MyComponent extends Component {
111+
// ...
112+
// });
113+
// var session = new Session();
114+
// React.render(<MyComponent rethinkSession={session} />, mountNode);
115+
export const PropsHoC = name => BaseHoC(component => component.props[name]);

src/index.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {BaseHoC, PropsHoC} from './HoC';
12
import {BaseMixin, PropsMixin} from './Mixin';
23
import {QueryRequest} from './QueryRequest';
34
import {MetaSession} from './Session';
@@ -12,24 +13,31 @@ const DefaultSession = new Session();
1213
// Singleton mixin for convenience, which uses the DefaultSession singleton as
1314
// the session.
1415
const DefaultMixin = BaseMixin(() => DefaultSession);
16+
const DefaultHoC = BaseHoC(() => DefaultSession);
1517

1618
const ReactRethinkdb = {
1719
BaseMixin,
1820
PropsMixin,
21+
BaseHoC,
22+
PropsHoC,
1923
QueryRequest,
2024
r,
2125
Session,
2226
DefaultSession,
23-
DefaultMixin
27+
DefaultMixin,
28+
DefaultHoC
2429
};
2530

2631
export {
2732
BaseMixin,
2833
PropsMixin,
34+
BaseHoC,
35+
PropsHoC,
2936
QueryRequest,
3037
r,
3138
Session,
3239
DefaultSession,
3340
DefaultMixin,
41+
DefaultHoC,
3442
ReactRethinkdb as default,
3543
};

0 commit comments

Comments
 (0)