首页 文章

在中间件中调度redux操作会导致意外行为

提问于
浏览
1

我试图理解redux中间件如何工作,在我的实验中我注意到从redux中间件调度一个动作可能会导致意外的行为 .

我将尝试通过模拟文件上传来解释此问题,如下所示:

我们有3个动作:

const setProgress = (progress) => ({ type: SET_UPLOAD_PROGRESS, progress });
const setThumbnail = (thumbnail) => ({ type: SET_THUMBNAIL, thumbnail });
const calculateTotal = () => ({ type: CALCULATE_TOTAL });

中间件计算总数:

export const testMiddleware = (store) => (next) => (action) => {
  if (action.type === 'CALCULATE_TOTAL') {
    return next(action);
  }
  const result = next(action);
  store.dispatch(calculateTotal());
  return result;
};

减速器:

const initialState = {
  progress: 0,
  total: 0,
  thumbnail: ''
};
export function uploadReducer(state = initialState, action) {
  switch (action.type) {
    case SET_UPLOAD_PROGRESS:
      state.progress = action.progress;
      return { ...state };
    case SET_THUMBNAIL:
      state.thumbnail = action.thumbnail;
      return { ...state };
    case CALCULATE_TOTAL:
      state.total += state.progress * 5;
      return { ...state };
    default:
      return state;
  }
}

这是模拟文件上传的代码:

let cnt = 0;
  // simulate upload progress
  const setNewProgress = () => {
    cnt += 2;
    if (cnt > 5) return;
    setTimeout(() => {
      store.dispatch(setProgress(cnt * 2));
      setNewProgress();
    }, 1000);
  };
  setNewProgress();
  // simulate thumbnail generating
  setTimeout(() => {
    store.dispatch(setThumbnail('blob:http://thumbnail.jpg'));
  }, 2500);

以下是事件序列:

第一个操作按预期工作并设置进度值:

问题从这里开始; thumbnail假设由'setThumbnail'设置,但devtools显示它已由'calculateTotal'设置,并且之后的每个调度都不匹配:

我在这做错了什么?它是按设计的吗?如何在不造成上述问题的情况下在中间件中调度操作?

1 回答

  • 1

    这种意外行为可能是因为 uploadReducer 不是pure,即它直接在你的州运作(例如 state.progress = action.progress; ) . Reducers should仅返回新状态,而不是通过redux修改注入reducer的现有状态 . 因此,你的减速机需要看起来像这样:

    export function uploadReducer(state = initialState, action) {
      switch (action.type) {
        case SET_UPLOAD_PROGRESS:
          return { ...state, progress: action.progress };
        case SET_THUMBNAIL:
          return { ...state, thumbnail: action.thumbnail };
        case CALCULATE_TOTAL:
          return { ...state, total: state.total + state.progress * 5 };
        default:
          return state;
      }
    }
    

    如何在不造成上述问题的情况下在中间件中调度操作?

    你的中间件看起来很好(你正确地防止了递归并且还返回了 next() 结果(这在你的例子中是不需要的,但在真正的应用程序中仍然有意义 . )你的行为看起来也很好(样式注释:你可以包装你的动作) payload 属性中的有效负载,这是一个常见的convention) .

相关问题