import createWatcherSaga, { TakeType } from './create-watcher-saga.js';
// Act on every counter increment
const watchIncrementSaga = createWatcherSaga({ takeType: TakeType.every }, incrementSaga, 'counter/increment');
// Discard previous user fetches and act only on the latest
const watchFetchUser = createWatcherSaga({ takeType: TakeType.latest }, fetchUserSaga, 'user/fetch');
// Validate sign-up e-mail while the user is typing, but wait the user to have stopped typing for 2 seconds to not flood the server.
const watchValidateSignUpEmailSaga = createWatcherSaga({ takeType: TakeType.debounce, timeout: 2000 }, validateSignUpEmailSaga, 'user/validateSignUp');
// Fetch exchange rates of the same pair of currencies at most once during 10 seconds.
const watchFetchRatesSaga = createWatcherSaga(
{
takeType: TakeType.throttleByKey,
timeout: 10000,
selector: action => `${action.payload.from}-${action.payload.to}`
},
fetchRatesSaga,
'currency/fetchRates'
);
Last active
September 4, 2020 14:38
-
-
Save hbarcelos/9880a469ed36edf3d7c98b6b80756651 to your computer and use it in GitHub Desktop.
Reducing boilerplate of watcher sagas in redux-saga
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
call, | |
cancelled, | |
debounce, | |
delay, | |
fork, | |
take, | |
takeEvery, | |
takeLatest, | |
takeLeading, | |
throttle, | |
} from 'redux-saga/effects'; | |
export const TakeType = { | |
every: 'every', | |
latest: 'latest', | |
leading: 'leading', | |
throttle: 'throttle', | |
debounce: 'debounce', | |
throttleByKey: 'throttleByKey', | |
}; | |
export default function createWatcherSaga({ takeType = TakeType.every, additionalArgs = [], timeout, selector }, saga, pattern) { | |
if ([TakeType.throttle, TakeType.debounde].includes(takeType)) { | |
if (timeout === undefined) { | |
throw new Error('Cannot use TakeType.throttle without specifying a timeout'); | |
} | |
} | |
if (takeType === TakeType.throttleByKey) { | |
if (timeout === undefined) { | |
throw new Error('Cannot use TakeType.throttleByKey without specifying a timeout'); | |
} | |
if (selector === undefined) { | |
throw new Error('Cannot use TakeType.throttleByKey without specifying a selector'); | |
} | |
} | |
const factory = sagaFactoryByType[takeType]; | |
if (!factory) { | |
throw new Error(`Unknown take type "${takeType}". Should be one of ${Object.keys(TakeType)}.`); | |
} | |
const watcherSaga = factory({ pattern, saga, additionalArgs, timeout, selector }); | |
const sagaName = saga.displayName ?? saga.name ?? '<anonymous>'; | |
return Object.defineProperty(watcherSaga, 'name', { value: `watcher(${sagaName})` }); | |
} | |
const sagaFactoryByType = { | |
every: ({ pattern, saga, additionalArgs }) => | |
function* watcherSaga() { | |
yield takeEvery(pattern, saga, ...additionalArgs); | |
}, | |
latest: ({ pattern, saga, additionalArgs }) => | |
function* watcherSaga() { | |
yield takeLatest(pattern, saga, ...additionalArgs); | |
}, | |
leading: ({ pattern, saga, additionalArgs }) => | |
function* watcherSaga() { | |
yield takeLeading(pattern, saga, ...additionalArgs); | |
}, | |
throttle: ({ timeout, pattern, saga, additionalArgs }) => | |
function* watcherSaga() { | |
yield throttle(timeout, pattern, saga, ...additionalArgs); | |
}, | |
debounce: ({ timeout, pattern, saga, additionalArgs }) => | |
function* watcherSaga() { | |
yield debounce(timeout, pattern, saga, ...additionalArgs); | |
}, | |
throttleByKey: ({ timeout, selector, pattern, saga, additionalArgs }) => | |
function* watcherSaga() { | |
const set = new Set(); | |
while (true) { | |
const action = yield take(pattern); | |
const id = selector(action); | |
const throttled = set.has(id); | |
try { | |
if (!throttled) { | |
set.add(id); | |
yield fork(function* () { | |
yield delay(timeout); | |
set.delete(id); | |
}); | |
yield call(saga, action, ...additionalArgs); | |
} | |
} finally { | |
if (yield cancelled()) { | |
set.delete(id); | |
} | |
} | |
} | |
}, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment