aikoven / typescript-fsa-redux-saga

TypeScript FSA utilities for redux-saga
MIT License
61 stars 4 forks source link

Usage with takeLatest/takeEvery #3

Closed rclmenezes closed 6 years ago

rclmenezes commented 6 years ago

Hi,

bindAsyncAction is pretty handy, but its resulting generator takes params as an input. This can be annoying when using something like Redux's takeLatest and takeEvery functions:

// actions.js
import actionCreatorFactory from 'typescript-fsa';

type LoginSubmit = { email: string, password: string };
type User = { email: string, id: number };

const actionCreator = actionCreatorFactory();
const loginSubmit = actionCreator<LoginSubmit>('LOGIN_SUBMIT');
const login = actionCreator.async<LoginSubmit, User>('LOGIN');

// sagas/login.ts
import { SagaIterator } from 'redux-saga';
import { call, takeLatest } from 'redux-saga/effects';
import { Action } from 'typescript-fsa';
import { bindAsyncAction } from 'typescript-fsa-redux-saga';

const loginWorker = bindAsyncAction(login)(
  function* (params): SagaIterator {
    const user = yield call(postLogin, params);
    yield user;
  },       
);

function loginSubmitSaga(action: Action<LoginSubmit>) {
  // wrap loginWorker so that it can take an action
  return loginWorker(action.payload)
}

takeLatest('LOGIN_SUBMIT', loginSubmitSaga);

However, I use takeLatest a lot and I don't want to import the payload every time I make a saga. I ended up making a function called bindWorkerToAction:

// utils.ts
function bindWorkerToAction<P>(worker: (P, ...rest: any[]) => SagaIterator) {
    return function(action: Action<P>) {
        return worker(action.payload)
    }
}

// sagas.ts
const loginSubmitSaga = bindWorkerToAction(loginWorker);

takeLatest('LOGIN_SUBMIT', loginSubmitSaga);

I think bindWorkerToAction is pretty handy. If you'd like, I can create a PR that adds it to this repo.

aikoven commented 6 years ago

How about this:

const loginWorker = bindAsyncAction(...)(...);

takeLatest('LOGIN_SUBMIT', action => loginWorker(action.payload))

It isn't much more verbose than your variant.

The only problem here is that the type of action would be a built-in Redux Action which doesn't have a payload. Luckily, redux-saga allows us to pass the action creator directly instead of action type string:

takeLatest(loginSubmit, action => ...);

Which allows us to do a smarter typing for takeLatest / takeEvery which will correctly infer the type of action. This is already done in the typings for upcoming redux-saga@1.0 (see https://github.com/redux-saga/redux-saga/pull/1255). Not sure when it will be released though.

rclmenezes commented 6 years ago

image

That's way more elegant. Thanks!

euroclydon37 commented 4 years ago

@aikoven Is this only when using libraries like the redux toolkit, where every action creator has toString() defined?