-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.jsx
114 lines (107 loc) · 3.08 KB
/
index.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import React from 'react';
import xs from 'xstream';
const Streams = {};
export const ACTION_CASCADE = 'ACTION_CASCADE';
/**
* Step 1. Action$
*
* Stream with incoming user actions.
*/
var Dispatcher;
export const Action$ = xs.create({
start: function(listener){
Dispatcher = listener;
},
stop: function(){
Dispatcher = null;
}
});
Action$.ofType = type => Action$
.filter(event => {
return typeof type === 'string'? event.type === type: type.indexOf(event.type) >= 0
})
.debug(`Action$[${type}]`);
/**
* Step 2. Export stream to React (a 'controller')
*
* Usage: stream.compose(toReact('name'))
*/
export const toReact = function(name){
return function(stream){
stream = stream.remember()
if(Streams[name]) {
const err = `toReact: ${name} already exists!`;
console.error(err);
this.error(err);
} else {
Streams[name] = stream;
}
return stream;
}
}
/**
* Step 3. Connect Raect Components to Rxjs
* using a Higher Order Component
*
* Usage: withStream(['ctrlName'])(Component)
*/
export const withStream = function(controllers){
return function (WrappedComponent){
let displayName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
displayName = `RxContainer(${displayName})`;
return React.createClass({
displayName: displayName,
componentWillMount: function(){
this._subscriptions = controllers.map(name => {
if(!Streams[name]){
throw new Error(
`${displayName} cannot find controller ` +
`${name} because observable.toReact('${name}') was never called.
Available controllers are: ${Object.keys(Streams).join(', ')}`
);
}
//console.log(`${displayName} subscribes to ${name}`);
return Streams[name].subscribe({
next: this.handleControllerUpdate,
error: this.handleControllerError
});
})
},
componentWillUnmount: function(){
//console.log('unsubscribe ',displayName);
this._subscriptions.forEach(sub => sub.unsubscribe())
},
handleControllerUpdate: function(value){
this.setState(value);
},
handleControllerError: function(error ){
this.setState({ error })
},
render() {
return <WrappedComponent {...this.props} {...this.state} />;
}
});
}
}
/**
* Step 4. Dispatch an event
*/
export const dispatch = function(action,data){
if(typeof action === 'object'){
data = action;
} else if(typeof action === 'string'){
data = data || {};
data.type = action;
}
// Unidirectional data-flow: an action can NEVER trigger another action
// You can trace everything from the Action stream down.
if(Action$.busy){
/* eslint no-console: "off" */
console.error('Action$: ACTION_CASCADE',[ Action$.busy, data.action ]);
Action$.error({ type: ACTION_CASCADE, actions: [ Action$.busy, data.action ]});
return;
}
Dispatcher.busy = data.action;
Dispatcher.next(data);
Dispatcher.busy = false;
}