How to dispatch a thunk from a saga?

Viewed 3119

I know I shouldn't be trying to dispatch thunks from sagas, it goes against what redux-saga tries to do. But I'm working in a fairly large app and most of the code is made with thunks, we are migrating by bits and need to dispatch a thunk from inside a saga. The thunk can't be changed because it is used in other parts (a thunk that returns a promise), so it would break many things.

configureStore:

const store = createStore(
  rootReducer,
  initialState,
  compose(applyMiddleware(thunk, sagaMiddleware))
);

Saga:

// Saga (is called from a takeEvery)
function* watchWarehouseChange(action) {
  const companyId = yield select(Auth.id);

  // We use cookies here instead of localStorage so that we persist
  // it even when the user logs out. (localStorage clears on logout)
  yield call(Cookies.set, `warehouse${companyId}`, action.warehouse);

  // I want to dispatch a thunk here
  yield put.resolve(syncItems);
  // put(syncItems) doesn't work either
}

Thunk:

export function syncItems() {
  console.log('first!');

  return dispatch => {
    console.log('second!');

    return dispatch(fetchFromBackend()).then(
      items => itemsDB.emptyAndFill(items)
    )
  }
}

Whenever syncItems() is executed, only first! logs. second! never happens.

PS: I don't get any errors or warnings.

3 Answers

If you read the documentation of redux-saga and specifically call and put:

call:

fn: Function - A Generator function, or normal function which either returns a Promise as result, or any other value.

put:

Creates an Effect description that instructs the middleware to put an action into the provided channel.

Technically a thunk returns a Promise, which is why you can await a dispatch of a thunk:

export declare type AsyncThunkAction<Returned, ThunkArg, ThunkApiConfig extends AsyncThunkConfig> = (dispatch: GetDispatch<ThunkApiConfig>, getState: () => GetState<ThunkApiConfig>, extra: GetExtra<ThunkApiConfig>) => Promise<ReturnType<AsyncThunkFulfilledActionCreator<Returned, ThunkArg>> | ReturnType<AsyncThunkRejectedActionCreator<ThunkArg, ThunkApiConfig>>> & {
    abort(reason?: string): void;
    requestId: string;
    arg: ThunkArg;
};

This means you can do the following to dispatch a thunk from a saga:

yield put(
  yield call(syncItems)
);

The call method returns the redux action of your syncItems saga, which will be used by the put method to dispatch your action.

Related