-
Notifications
You must be signed in to change notification settings - Fork 11
/
sagaInjectors.js
124 lines (104 loc) · 3.38 KB
/
sagaInjectors.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
import invariant from 'invariant';
import { isEmpty, isFunction, isString, conformsTo } from 'lodash';
import checkStore from './checkStore';
import { DAEMON, ONCE_TILL_UNMOUNT, RESTART_ON_REMOUNT } from './constants';
const allowedModes = [RESTART_ON_REMOUNT, DAEMON, ONCE_TILL_UNMOUNT];
const checkKey = (key) =>
invariant(isString(key) && !isEmpty(key), '(app/utils...) injectSaga: Expected `key` to be a non empty string');
const checkDescriptor = (descriptor) => {
const shape = {
saga: isFunction,
mode: (mode) => isString(mode) && allowedModes.includes(mode)
};
invariant(conformsTo(descriptor, shape), '(app/utils...) injectSaga: Expected a valid saga descriptor');
};
/**
* Validate the saga, mode and key
* @param {object} descriptor The saga descriptor
* @param {string} key The saga key
* @param {object} saga The saga
*/
export function injectSagaFactory(store, isValid) {
const updateHasSagaInDevelopment = (hasSaga, key, saga) => {
const oldDescriptor = store.injectedSagas[key];
// enable hot reloading of daemon and once-till-unmount sagas
if (hasSaga && oldDescriptor.saga !== saga) {
oldDescriptor.task.cancel();
return false;
}
return hasSaga;
};
const updateStoreInjectors = (newDescriptor, saga, key, args) => {
store.injectedSagas[key] = {
...newDescriptor,
task: store.runSaga(saga, args)
};
};
const checkAndUpdateStoreInjectors = (hasSaga, key, newDescriptor, args) => {
if (!hasSaga || (hasSaga && newDescriptor.mode !== DAEMON && newDescriptor.mode !== ONCE_TILL_UNMOUNT)) {
updateStoreInjectors(newDescriptor, newDescriptor.saga, key, args);
}
};
return function injectSaga(key, descriptor = {}, args) {
if (!isValid) {
checkStore(store);
}
const newDescriptor = {
...descriptor,
mode: descriptor.mode || DAEMON
};
checkKey(key);
checkDescriptor(newDescriptor);
let hasSaga = Reflect.has(store.injectedSagas, key);
if (process.env.NODE_ENV !== 'production') {
hasSaga = updateHasSagaInDevelopment(hasSaga, key, newDescriptor.saga);
}
checkAndUpdateStoreInjectors(hasSaga, key, newDescriptor, args);
return key;
};
}
/**
* Eject the saga
* @param {string} key The saga key
* @param {object} store The redux store
* @param {boolean} isValid If the store is valid
*/
export function ejectSagaFactory(store, isValid) {
/**
* Clean up the store
* @param {string} key The saga key
* @returns {void}
*/
function updateStoreSaga(key) {
// Clean up in production; in development we need `descriptor.saga` for hot reloading
if (process.env.NODE_ENV === 'production') {
// Need some value to be able to detect `ONCE_TILL_UNMOUNT` sagas in `injectSaga`
store.injectedSagas[key] = 'done';
}
}
return function ejectSaga(key) {
if (!isValid) {
checkStore(store);
}
checkKey(key);
if (Reflect.has(store.injectedSagas, key)) {
const descriptor = store.injectedSagas[key];
if (descriptor.mode && descriptor.mode === DAEMON) {
return;
}
descriptor.task.cancel();
updateStoreSaga(key);
}
};
}
/**
* Get the injectors
* @param {object} store The redux store
*/
export default function getInjectors(store) {
checkStore(store);
return {
injectSaga: injectSagaFactory(store, true),
ejectSaga: ejectSagaFactory(store, true)
};
}