Reputation: 1939
I have a situation where I have a function (named onGetCameras
) that accepts a callback function (e.g., getCamerasSuccess
); the intention is to call onGetCameras
(which is an external function), which, does an AJAX call, and upon completion, calls getCamerasSuccess
, passing the result it received.
What I have is something like this:
// My 'actionCreators' variable is a reference to create my actions
function* getCamerasSaga(action: action.GetCameras) {
const { onGetCameras } = action;
const getCamerasSuccess = function*(response: GetCamerasResponse) {
console.log('SUCCESS', response);
yield put(actionCreators.getCamerasSuccess(response);
}
yield put(actionCreators.getCamerasStart());
yield call(onGetCameras, getCamerasSuccss);
}
export function* watchCamera() {
yield takeEvery(ActionTypes.GetCameras, getCamerasSaga);
}
I can't figure out why it's not going into my getCamerasSuccess
function: I never see my console.log
message inside of that function
But, if I change my success callback to be a normal function, e.g.:
const getCamerasSuccess = (response: GetCamerasResponse) => {
console.log('RESPONSE', response);
}
I can see that I'm receiving my response, but, as I mentioned, using the generator function, it seems that it never goes inside of that function.
Any help is deeply appreciated.
Upvotes: 3
Views: 2689
Reputation: 4985
Your getCamerasSuccess
isn't called because when you call a function it gets executed but when you call a generator function it only returns an iterator object on which you have to keep calling next
for it to execute.
Your code would still not work though, because you are trying to use saga effect in a generator that is not under redux-saga control. If you want to keep working with callbacks you might be interested in the cps effect ( https://redux-saga.js.org/docs/api/#cpsfn-args ). The callback has to be in node.js style though (first param err, second result).
Your code could then look like this:
function* getCamerasSaga(action: action.GetCameras) {
const { onGetCameras } = action;
yield put(actionCreators.getCamerasStart());
try {
const response: GetCamerasResponse = yield cps(onGetCameras);
console.log('SUCCESS', response);
yield put(actionCreators.getCamerasSuccess(response));
} catch(err) { /* ... */ }
}
If modifying the onGetCameras
API isn't an option you will have to either use normal function as callback and then use store.dispatch
instead of put
or you can create some small utility function with eventChannel (https://redux-saga.js.org/docs/advanced/Channels.html).
E.g.:
...
import { eventChannel, END } from 'redux-saga';
function createCbChannel(fn) {
return eventChannel(emitter => {
fn((response) => {
emitter(response);
emitter(END);
});
return () => emitter(END);
});
}
function* getCamerasSaga(action: action.GetCameras) {
const { onGetCameras } = action;
yield put(actionCreators.getCamerasStart());
const cbChannel = yield call(createCbChannel, onGetCameras);
const response: GetCamerasResponse = yield take(cbChannel);
console.log('SUCCESS', response);
yield put(actionCreators.getCamerasSuccess(response));
}
Upvotes: 3