【问题标题】:Redux-Saga is running twice for a single actionRedux-Saga 为单个操作运行两次
【发布时间】:2016-09-19 04:47:33
【问题描述】:

我有一个传奇来处理类似的请求。用户单击按钮来切换照片的点赞状态。

saga 监听SUBMIT_LIKE 类型的动作。我的问题是 submitLikeSaga 为每个 SUBMIT_LIKE 操作运行两次。 例如在 api-error 情况下,一个 SUBMIT_LIKE 操作会触发两个 api 调用和四个 RECEIVE_LIKE 操作。

(如果有帮助,请使用 react-boilerplate。)

export function* submitLikeSaga(action) {

  // optimistically update the UI
  // action shape: {
  //    type: RECEIVE_LIKE,
  //    like: {id: 1, liked: true}
  //  }
  yield put(receiveLike(action.like));

  // POST like data to api
  const response = yield call(
    request,
    `${API_ENDPOINT}/user_likes.json`,
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${action.token}`,
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(action.like),
    }
  );

  // if api call failed, reverse change made to UI
  if (response.err) {
    yield put(receiveLike({
      id: action.like.id,
      liked: !action.like.liked,
    }));
  }
}

export function* watchSubmitLike() {
  yield* takeEvery(SUBMIT_LIKE, submitLikeSaga);
}

// All sagas to be loaded
export default [
  watchFetchUsers,
  watchSubmitLike,
];

编辑:添加中间件并查看代码。

ProfileGrid.js

const ProfileGrid = ({
  users,
  submitLike,
  token,
}) =>
  <div className={styles.profileGrid}>
    {users.map((user, i) => (
      <div key={i} className={styles.gridTile}>
        <GridTile
          title={user.username}
          actionIcon={<ActionIcon
            onIconClick={() => { submitLike(user.id, !user.liked, token); }}

            isActive={user.liked}
            activeColor="yellow"
            defaultColor="white"
          />}
        >
          <img style={{ width: '100%', height: 'auto' }} src={user.avatar} alt="profile" />
        </GridTile>
      </div>
    ))}
  </div>;

ActionIcon.js

const ActionIcon = ({

  onIconClick,
  isActive,
  activeColor,
  defaultColor,
}) =>
  <IconButton onClick={onIconClick} >
    <StarBorder
      color={isActive ? activeColor : defaultColor}
    />
  </IconButton>;

store.js

/**
 * Create the store with asynchronously loaded reducers
 */

import { createStore, applyMiddleware, compose } from 'redux';
import { fromJS } from 'immutable';
import { routerMiddleware } from 'react-router-redux';
import createSagaMiddleware from 'redux-saga';
import createReducer from './reducers';

const sagaMiddleware = createSagaMiddleware();
const devtools = window.devToolsExtension || (() => (noop) => noop);

export default function configureStore(initialState = {}, history) {
  // Create the store with two middlewares
  // 1. sagaMiddleware: Makes redux-sagas work
  // 2. routerMiddleware: Syncs the location/URL path to the state
  const middlewares = [
    sagaMiddleware,
    routerMiddleware(history),
  ];

  const enhancers = [
    applyMiddleware(...middlewares),
    devtools(),
  ];

  const store = createStore(
    createReducer(),
    fromJS(initialState),
    compose(...enhancers)
  );

  // Extensions
  store.runSaga = sagaMiddleware.run;
  store.asyncReducers = {}; // Async reducer registry

  // Make reducers hot reloadable, see http://mxs.is/googmo
  /* istanbul ignore next */
  if (module.hot) {
    module.hot.accept('./reducers', () => {
      System.import('./reducers').then((reducerModule) => {
        const createReducers = reducerModule.default;
        const nextReducers = createReducers(store.asyncReducers);

        store.replaceReducer(nextReducers);
      });
    });
  }

  return store;
}

asyncInjectors.js

import { conformsTo, isEmpty, isFunction, isObject, isString } from 'lodash';
import invariant from 'invariant';
import warning from 'warning';
import createReducer from '../reducers';

/**
 * Validate the shape of redux store
 */
export function checkStore(store) {
  const shape = {
    dispatch: isFunction,
    subscribe: isFunction,
    getState: isFunction,
    replaceReducer: isFunction,
    runSaga: isFunction,
    asyncReducers: isObject,
  };
  invariant(
    conformsTo(store, shape),
    '(app/utils...) asyncInjectors: Expected a valid redux store'
  );
}

/**
 * Inject an asynchronously loaded reducer
 */
export function injectAsyncReducer(store, isValid) {
  return function injectReducer(name, asyncReducer) {
    if (!isValid) checkStore(store);

    invariant(
      isString(name) && !isEmpty(name) && isFunction(asyncReducer),
      '(app/utils...) injectAsyncReducer: Expected `asyncReducer` to be a reducer function'
    );

    store.asyncReducers[name] = asyncReducer; // eslint-disable-line no-param-reassign
    store.replaceReducer(createReducer(store.asyncReducers));
  };
}

/**
 * Inject an asynchronously loaded saga
 */
export function injectAsyncSagas(store, isValid) {
  return function injectSagas(sagas) {
    if (!isValid) checkStore(store);

    invariant(
      Array.isArray(sagas),
      '(app/utils...) injectAsyncSagas: Expected `sagas` to be an array of generator functions'
    );

    warning(
      !isEmpty(sagas),
      '(app/utils...) injectAsyncSagas: Received an empty `sagas` array'
    );

    sagas.map(store.runSaga);
  };
}

/**
 * Helper for creating injectors
 */
export function getAsyncInjectors(store) {
  checkStore(store);

  return {
    injectReducer: injectAsyncReducer(store, true),
    injectSagas: injectAsyncSagas(store, true),
  };
}

【问题讨论】:

  • 该代码对我来说看起来不错...您能提供更多代码吗?如何应用 saga 中间件和实际触发 SUBMIT_LIKE 事件的组件会有所帮助
  • @DrewSchuster 是的,它与运行良好的登录代码非常相似。我添加了一些视图和存储代码。
  • 我也有同样的问题。你得到这个解决方案了吗?

标签: javascript reactjs redux redux-saga react-boilerplate


【解决方案1】:

根据文档:https://redux-saga.js.org/docs/api/index.html#takelatestpattern-saga-args 这将接听您的最新电话,并且只会触发一项操作

import { fork, takeLatest } from 'redux-saga/effects';

export function* watchSubmitLike() {
  yield fork(takeLatest, SUBMIT_LIKE, submitLikeSaga);

}

【讨论】:

    【解决方案2】:

    如果您进行以下操作,您的应用程序将正常运行

    yield* takeEvery(SUBMIT_LIKE, submitLikeSaga);应该是

    yield takeEvery(SUBMIT_LIKE, submitLikeSaga);

    只有在对 sagas 进行测序时才应该使用 yield*

    【讨论】:

      【解决方案3】:

      我最近发生了这种情况。在我的例子中,这个传奇被创建了两次。我们我们withSagaHOC(一般在容器中)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-15
        • 1970-01-01
        • 2020-05-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多