piotrwitek / typesafe-actions

Typesafe utilities for "action-creators" in Redux / Flux Architecture

Home Page:https://codesandbox.io/s/github/piotrwitek/typesafe-actions/tree/master/codesandbox

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add a generic redux-saga factory automatically handling async-actions - createAsyncSaga

edgarSang opened this issue · comments

I used typescript typesafe-actions v4

and I created Saga common function

like this

export default function createAsyncSaga<T1, P1, T2, P2, T3, P3>(
  asyncActionCreator: AsyncActionCreator<[T1, P1], [T2, P2], [T3, P3]>,
  promiseCreator: PromiseCreatorFunction<P1, P2>,
) {
  return function* saga(action: ReturnType<typeof asyncActionCreator.request>) {
    try {
      const result = isPayloadAction<P1>(action)
        ? yield call(promiseCreator, action.payload)
        : yield call(promiseCreator);
      yield put(asyncActionCreator.success(result));
    } catch (e) {
      yield put(asyncActionCreator.failure(e));
    }
  };
}

With this common function, I was able to create a single line of sagas.

like this

const getUserProfileSaga = createAsyncSaga(getUserProfileAsync, getUserProfile);

but in v5 i can't use this source code.

becuase of AsyncActionCreatorBuilder
(error message is Type '[T1, P1]' does not satisfy the constraint '[T1, [T1, P1] ....ommited..)

How do I change my source code??

Hey @edgarSang,
I will try to help you but I need all the missing details like:

  • what is PromiseCreatorFunction<P1, P2> type
  • what is isPayloadAction

I might even consider adding it to the library as a build-in helper function.
I'll be waiting for your response.

hi, here is v5 createAsyncSaga. it worked.

interface AsyncActionGroup<
  T1 extends string,
  P1,
  T2 extends string,
  P2,
  T3 extends string,
  P3
> {
  request: EmptyActionCreator<T1> | PayloadActionCreator<T1, P1>;
  success: EmptyActionCreator<T2> | PayloadActionCreator<T2, P2>;
  failure: EmptyActionCreator<T3> | PayloadActionCreator<T3, P3>;
}

type PromiseCreatorFunction<P, T> =
  | ((payload: P) => Promise<T>)
  | (() => Promise<T>);

function isPayloadAction<P>(action: any): action is PayloadAction<string, P> {
  return action.payload !== undefined;
}

export default function createAsyncSaga<
  T1 extends string,
  P1,
  T2 extends string,
  P2,
  T3 extends string,
  P3
>(
  asyncActionGroup: AsyncActionGroup<T1, P1, T2, P2, T3, P3>,
  promiseCreator: PromiseCreatorFunction<P1, P2>,
) {
  return function* saga(action: ReturnType<typeof asyncActionGroup.request>) {
    try {
      const result = isPayloadAction<P1>(action)
        ? yield call(promiseCreator, action.payload)
        : yield call(promiseCreator);

      yield put(asyncActionGroup.success(result));
    } catch (e) {
      yield put(asyncActionGroup.failure(e));
    }
  };
}

I think this function is useful for reducing boilerplate, but I don't think it's suitable as a built-in helper. This is because there is more work to modify the boilerplate itself, such as state management of asynchronous requests, logging, and selector mapping.