typescript-fsa-redux-saga
Version:
TypeScript FSA utilities for redux-saga
81 lines (72 loc) • 2.27 kB
text/typescript
import {AsyncActionCreators} from 'typescript-fsa';
import {SagaIterator} from 'redux-saga';
import {put, call, cancelled} from 'redux-saga/effects';
export interface BindAsyncActionOptions {
skipStartedAction?: boolean;
}
export function bindAsyncAction<R>(
actionCreators: AsyncActionCreators<void, R, any>,
options?: BindAsyncActionOptions,
): {
<Args extends any[]>(
worker: (params: void, ...args: Args) => Promise<R> | SagaIterator,
): (params: void, ...args: Args) => SagaIterator;
(worker: () => Promise<R> | SagaIterator): () => SagaIterator;
};
export function bindAsyncAction<P, R>(
actionCreators: AsyncActionCreators<P, R, any>,
options?: BindAsyncActionOptions,
): {
<Args extends any[]>(
worker: (params: P, ...args: Args) => Promise<R> | SagaIterator,
): (params: P, ...args: Args) => SagaIterator;
};
export function bindAsyncAction(
actionCreator: AsyncActionCreators<any, any, any>,
options: BindAsyncActionOptions = {},
) {
return (
worker: (params: any, ...args: any[]) => Promise<any> | SagaIterator,
) => {
function* boundAsyncActionSaga(params: any, ...args: any[]): SagaIterator {
if (!options.skipStartedAction) {
yield put(actionCreator.started(params));
}
try {
const result = yield (call as any)(worker, params, ...args);
yield put(actionCreator.done({params, result}));
return result;
} catch (error) {
yield put(actionCreator.failed({params, error}));
throw error;
} finally {
if (yield cancelled()) {
yield put(actionCreator.failed({params, error: 'cancelled'}));
}
}
}
const capName =
worker.name.charAt(0).toUpperCase() + worker.name.substring(1);
return setFunctionName(
boundAsyncActionSaga,
`bound${capName}(${actionCreator.type})`,
);
};
}
/**
* Set function name.
*
* Note that this won't have effect on built-in Chrome stack traces, although
* useful for stack traces generated by `redux-saga`.
*/
function setFunctionName<F extends Function>(func: F, name: string): F {
try {
Object.defineProperty(func, 'name', {
value: name,
configurable: true,
});
} catch (e) {
// ignore
}
return func;
}