I've implement a redux effect takeLeading
that will ignore subsequent actions if the saga is currently running:
export const takeLeading = (patternOrChannel, saga, ...args) => fork(function*() {
while (true) {
const action = yield take(patternOrChannel);
yield call(saga, ...args.concat(action));
}
});
I use this for API fetching in my application, where each endpoint in my API has its own action type. So for GET
methods it's useful to block if the request has already been dispatched somewhere else in the app. The saga looks like:
return function* () {
yield all([takeLeading(GET_USER_ID, callApiGen), takeLeading(GET_WIDGET_ID, callApiGen)]);
}
The obvious problem is that if I want to get two different user IDs, the second will block because it too has action type GET_USER_ID. Short of making a different action for each possible parameter, is there a way to implement some takeLeadingForFunc(<action>, (action) => <id>, saga)
that allows me to keep the concise format of specifying one effect per request type but allows me to not block if the <id>
is different? I was trying to wrap takeLeading
with takeEvery
to implement something but couldn't quite get it.
EDIT:
I got something like this to work:
export const takeLeadingForFunc = (f) => (patternOrChannel, saga, ...args) => fork(function*() {
let takeLeadings = {};
while (true) {
const action = yield take(patternOrChannel);
if (!(f(action) in takeLeadings)) {
yield call(saga, ...args.concat(action))
takeLeadings[f(action)] = yield takeLeading((ac) => f(ac) === f(action) && ac.type === action.type, saga, ...args)
}
}
});
Which takes an extractor function f
that should return a primitive. This feels kind of hacky, so was wondering if there's a more idiomatic way to do this.