-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfetch.js
133 lines (108 loc) · 3.96 KB
/
fetch.js
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import Promise from 'bluebird';
import React, { Component, PropTypes } from 'react';
import hoistStatics from 'hoist-non-react-statics';
import invariant from 'invariant';
import storeShape from 'react-redux/lib/utils/storeShape';
import { bindActionCreators } from 'redux';
import { prefetchInit, prefetchSuccess, prefetchError, prefetchReset } from './reducer.js';
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
function getStateDefault(store) {
return store.getState().prefetching;
}
export default function fetch(_namespace, action, options = {}, getState = getStateDefault) {
const namespace = `${_namespace}_fetching`;
const { withRef = false } = options;
function createWaitFor(store) {
return function waitFor(...dependencies) {
const state = getState(store);
const states = dependencies
.map(dependency => state[`${dependency}_fetching`] && state[`${dependency}_fetching`].promise)
.filter(stateHolder => !!stateHolder);
if (states.length === 0) {
return Promise.resolve();
}
return Promise.all(states).then(() => {
const nextState = getState(store);
return dependencies.map(dependency => {
const result = nextState[`${dependency}_fetching`];
if (result.error) {
return Promise.reject(result.data);
}
return result.data;
});
});
};
}
function fetchData(store, params, state) {
// shortcut
if (state && state.fetching === false) {
return Promise[state.error ? 'reject' : 'resolve'](state.data);
}
// this must either return a promise
// or null, then it will be marked as resolved
const promise = action({ ...store, waitFor: createWaitFor(store) }, params, state);
// this allows us to wait in other handlers
store.dispatch(prefetchInit(namespace, promise));
// wait for the action to complete and emit success/error events
return Promise.resolve(promise).reflect().then(data => {
let act;
let value;
if (data.isFulfilled()) {
act = prefetchSuccess;
value = data.value();
} else {
act = prefetchError;
value = data.reason();
}
store.dispatch(act(namespace, value));
return value;
});
}
return function wrapWithFetch(WrappedComponent) {
class Fetch extends Component {
static displayName = `Fetch(${getDisplayName(WrappedComponent)})`;
static propTypes = {
store: storeShape,
params: PropTypes.object,
};
static contextTypes = {
store: storeShape,
};
constructor(props, context) {
super(props, context);
this.store = props.store || context.store;
this.prefetchReset = bindActionCreators(prefetchReset, this.store.dispatch);
invariant(this.store,
`Could not find "store" in either the context or ` +
`props of "${this.constructor.displayName}". ` +
`Either wrap the root component in a <Provider>, ` +
`or explicitly pass "store" as a prop to "${this.constructor.displayName}".`
);
}
componentDidMount() {
fetchData(this.store, this.props.params, this.getFetchProps());
}
getFetchProps() {
return getState(this.store)[namespace];
}
getWrappedInstance() {
invariant(withRef,
`To access the wrapped instance, you need to specify ` +
`{ withRef: true } as the fourth argument of the connect() call.`
);
return this.refs.wrappedInstance;
}
static fetch = fetchData;
static WrappedComponent = WrappedComponent;
render() {
const ref = withRef ? 'wrappedInstance' : null;
return (
<WrappedComponent {...this.props} fetch={this.getFetchProps()} prefetchReset={this.prefetchReset} ref={ref} />
);
}
}
return hoistStatics(Fetch, WrappedComponent);
};
}