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

以下是事件的顺序:

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

在此处输入图像描述

问题从这里开始;缩略图假设由“setThumbnail”设置,但 devtools 显示它已由“calculateTotal”设置,之后的每个调度都不匹配:

在此处输入图像描述

我在这里做错了什么?是设计使然吗?如何在不引起上述问题的情况下在中间件中调度操作?

4

1 回答 1

1

这种意外行为可能是由于您uploadReducer不纯即它直接在您的状态上运行(例如state.progress = action.progress;)。Reducers应该只返回新状态,而不是修改由 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属性中,这是一个常见的约定)。

于 2018-12-08T21:14:14.673 回答