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,
pose(applyMiddleware(thunk, sagaMiddleware))
);
Saga:
// Saga (is called from a takeEvery)
function* watchWarehouseChange(action) {
const panyId = 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${panyId}`, 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.
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,
pose(applyMiddleware(thunk, sagaMiddleware))
);
Saga:
// Saga (is called from a takeEvery)
function* watchWarehouseChange(action) {
const panyId = 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${panyId}`, 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.
Share Improve this question asked Jun 2, 2017 at 16:59 Mathius17Mathius17 2,5022 gold badges24 silver badges34 bronze badges3 Answers
Reset to default 11You're using syncItems
wrong. The key is that the function returned by syncItems
needs to get passed to dispatch
, not syncItems
itself. The correct usage would be:
yield put(syncItems());
I showed some visual parisons of how values are passed into dispatch
in my blog post Idiomatic Redux: Why use action creators? (based on an example gist I put together). Here's the examples:
// approach 1: define action object in the ponent
this.props.dispatch({
type : "EDIT_ITEM_ATTRIBUTES",
payload : {
item : {itemID, itemType},
newAttributes : newValue,
}
});
// approach 2: use an action creator function
const actionObject = editItemAttributes(itemID, itemType, newAttributes);
this.props.dispatch(actionObject);
// approach 3: directly pass result of action creator to dispatch
this.props.dispatch(editItemAttributes(itemID, itemType, newAttributes));
// parallel approach 1: dispatching a thunk action creator
const innerThunkFunction1 = (dispatch, getState) => {
// do useful stuff with dispatch and getState
};
this.props.dispatch(innerThunkFunction1);
// parallel approach 2: use a thunk action creator to define the function
const innerThunkFunction = someThunkActionCreator(a, b, c);
this.props.dispatch(innerThunkFunction);
// parallel approach 3: dispatch thunk directly without temp variable
this.props.dispatch(someThunkActionCreator(a, b, c));
In your case, just substitute yield put
for this.props.dispatch
, since you're dispatching from a saga instead of a connected ponent.
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.
use https://github./czewail/bind-promise-to-dispatch package
add resolve and reject paramers in saga func
then use this package func wrap this.props.dispatch
then you can use it with promise