【问题标题】:Redux-Saga: Async Task being called twiceRedux-Saga:异步任务被调用两次
【发布时间】:2018-09-06 15:06:15
【问题描述】:

我是 redux-saga 的新手,并试图在我的 React 应用程序中替换 redux-thunk。出于某种奇怪的原因,我的异步任务(worker)被执行了两次(但只有当内部有 AJAX 请求时——当有正常的副作用时不会发生,例如 console.log)。

从下面可以看出,我在 componentDidMount 中只调用了一次 getChildData。但是,我的 getChildDataAsync 生成器函数执行了两次(除非我注释掉 getChildData(这是一个返回承诺的异步请求)。该请求有效并且我看到了我的响应数据,但随后又触发了另一个请求。

起初我认为这可能与位置更改有关,但这种情况发生在首次访问网站/硬刷新时​​。

这是我设置 saga 的方式:

saga.js

import axios from 'axios';
import { call, put, take, cancel, takeLatest } from 'redux-saga/effects';
import { push, LOCATION_CHANGE } from 'react-router-redux';
import {
  CHILD_DATA_LOADING,
  CHILD_DATA_SUCCESS,
  CHILD_DATA_ERROR,
} from 'src/redux/actions/actionTypes';
import { getChildData } from 'src/api/search';

// Worker Saga - API Request
export function* getChildDataAsync(action) {
  const { id } = action;
  const params = { id, storageKeys, dataCategory, from };

  try {
    const response = yield call(getChildData, params);
    yield put({
      type: CHILD_DATA_SUCCESS,
      childData: response.data,
    });
  } catch (error) {
    yield put({
      type: CHILD_DATA_ERROR,
      childDataError: true,
      error,
    });
  }
}

// Watcher Saga
export function* watchGetChildData() {
  const childWatcher = yield takeLatest(CHILD_DATA_SUCCESS, getChildDataAsync);
}

// Root Saga - Generator Function
export default function* rootSaga() {
  yield [
    // Combine all watcher sagas
    watchGetChildData(),
  ];
}

store.js

// @flow
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import createHistory from 'history/createHashHistory';
import rootReducer from 'src/redux/reducers/index';
import { createLogger } from 'redux-logger';
import createSagaMiddleware from 'redux-saga';
import rootSaga from 'src/redux/sagas/sagas';
import thunk from 'redux-thunk';

// React Router Redux
export const history = createHistory();

// Redux Saga
const sagasMiddleware = createSagaMiddleware();

// Add Redux Logging Only to DEV/TST Environments
const middleware = applyMiddleware(thunk, sagasMiddleware, routerMiddleware(history));

// Create store
const store = createStore(rootReducer, middleware);

// Run Redux-Saga Watchers
sagasMiddleware.run(rootSaga);

export default store;

childDataCreator.js

// @flow
import {
  CHILD_DATA_SUCCESS,
  CHILD_DATA_ERROR,
} from 'src/redux/actions/actionTypes';


// Description: Async Request to Get Child Data for Vendors/Sets
export function getChildData(
  id: string,
) {
  return { type: CHILD_DATA_SUCCESS, id };
}

反应组件

class DataView extends Component<Props> {
  componentDidMount() {
      const _id = '12345';
      this.props.getChildData(_id);
    }
  }

  rest of class....

【问题讨论】:

  • 如果您有 Plunker 示例或类似的东西,将会很有帮助并且更快。

标签: redux-saga


【解决方案1】:

我认为最初的 getChildData 调用应该触发类似 CHILD_DATA_REQUEST 的动作,这是传奇应该关注的动作。

// Description: Async Request to Get Child Data for Vendors/Sets
export function getChildData(
  id: string,
) {
  return { type: CHILD_DATA_REQUEST, id };
}

// In your saga watcher function
const childWatcher = yield takeLatest(CHILD_DATA_REQUEST, getChildDataAsync);

一旦触发,getChildDataAsync 将被调用,CHILD_DATA_SUCCESS 将在请求成功时被触发,我猜这是预期的行为(考虑到常见的动作命名是有道理的)。

【讨论】: