我试图理解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 回答
这种意外行为可能是因为
uploadReducer
不是pure,即它直接在你的州运作(例如state.progress = action.progress;
) . Reducers should仅返回新状态,而不是通过redux修改注入reducer的现有状态 . 因此,你的减速机需要看起来像这样:你的中间件看起来很好(你正确地防止了递归并且还返回了
next()
结果(这在你的例子中是不需要的,但在真正的应用程序中仍然有意义 . )你的行为看起来也很好(样式注释:你可以包装你的动作)payload
属性中的有效负载,这是一个常见的convention) .