網站搜尋

reactjs - 在 redux-saga 中使用回調時的問題


我有一個情況,我有一個函數(名為 onGetCameras)接受一個回調函數(例如 getCamerasSuccess);其目的是調用 onGetCameras(這是一個外部函數),它會進行 AJAX 調用,並在完成後調用 getCamerasSuccess,傳遞它收到的結果。

我所擁有的東西像這樣:

// 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);
}

我無法弄清楚為什麼它沒有進入我的 getCamerasSuccess 函數:我從未看到那個函數內的 console.log 訊息

但是,如果我將我的成功回調改為普通函數,例如:

const getCamerasSuccess = (response: GetCamerasResponse) => {
  console.log('RESPONSE', response);
}

我可以看到我正在收到我的回應,但如我所提到的,使用生成器函數時,似乎它從未進入那個函數內。

任何幫助都深表感謝。

答案#1

你的 getCamerasSuccess 沒有被調用,因為當你調用一個函數時它會被執行,但當你調用一個生成器函數時,它只會返回一個迭代器對象,你必須不斷調用 next 才能執行它。

不過你的代碼仍然無法運作,因為你正試圖在不受 redux-saga 控制的生成器中使用 saga effect。如果你想繼續使用回調,你可能會對 cps effect 感興趣(https://redux-saga.js.org/docs/api/#cpsfn-args)。然而,回調必須是 node.js 樣式的(第一個參數為錯誤,第二個參數為結果)。

你的程式碼接著可以看起來像這樣:

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) { /* ... */ }
}

如果修改 onGetCameras API 不是選項,你將不得不使用普通函數作為回調,然後使用 store.dispatch 代替 put,或者你可以使用 eventChannel (https://redux-saga.js.org/docs/advanced/Channels.html) 創建一些小型的輔助函數。

例如:

...
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));
}