Skip to content

Instantly share code, notes, and snippets.

@superpowered
Last active December 12, 2019 01:29
Show Gist options
  • Save superpowered/82059154bc8c815908b538737d8af0c4 to your computer and use it in GitHub Desktop.
Save superpowered/82059154bc8c815908b538737d8af0c4 to your computer and use it in GitHub Desktop.
redux-saga helper function.
/*
takeLatestCancelablePerKey
Works like `takeLatest`, but can filter and cancel tasks based on a unique identifier.
Useful if you have multiple unique task ids firing off the same action but need them to be cancelable
and fireable seperately.
Code Sandbox example: https://codesandbox.io/s/distracted-faraday-gjz40
*/
import {
call,
cancel,
delay,
fork,
put,
take,
cancelled
} from "redux-saga/effects";
// ============= Main Function =================
function* cancelableTask(
worker,
action,
keySelector,
key,
cancelPatternOrChannel,
...args
) {
const task = yield fork(worker, ...args, action);
yield take(cancelAction => {
return (
cancelAction.type === cancelPatternOrChannel &&
keySelector(cancelAction) === key
);
});
if (task && task.isRunning()) {
yield cancel(task);
}
}
function takeLatestCancelablePerKey(
patternOrChannel,
cancelPatternOrChannel,
worker,
keySelector,
...args
) {
return fork(function*() {
const tasks = {};
while (true) {
const action = yield take(patternOrChannel);
const key = yield call(keySelector, action);
if (tasks[key] && tasks[key].isRunning()) {
yield cancel(tasks[key]);
}
tasks[key] = yield fork(
cancelableTask,
worker,
action,
keySelector,
key,
cancelPatternOrChannel,
...args
);
}
});
}
// ============= Example usage =================
function* incrementAsync({ id }) {
try {
console.log("I AM STARTING TO INCREMENT", id);
yield delay(5000);
yield put({ type: "INCREMENT" });
console.log("I AM FINISHED INCREMENTING", id);
} catch (error) {
console.log("ERRORED", id, error);
} finally {
if (yield cancelled()) {
console.log("CANCELLED", id);
}
}
}
function selectId({ id }) {
return id;
}
export default function* rootSaga() {
while (true) {
const task = yield takeLatestCancelablePerKey(
"INCREMENT_ASYNC",
"CANCEL_INCREMENT",
incrementAsync,
selectId
);
yield take("CANCEL_ALL_INCREMENTS");
yield cancel(task);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment