Обновление: я использую React-Boilerplate, не измененный от оригинала, за исключением контейнеров / компонентов. Редукторы запускаются несколько раз, иногда более двух раз, когда новое действие отправляется впервые, но не тогда, когда одно и то же действие отправляется впоследствии. Сами действия не выполняются при повторных вызовах редуктора, но состояние обновляется и повторно выполняет визуализацию компонента.
Например: если я отправлю action1, который обновляет reducerCase1, а не action2, который обновляет reducerCase2, action1 будет выполняться один раз, а reducerCase1 будет выполняться дважды. action2 и reducerCase2 не будут работать. Если я затем отправлю action3, который обновляет reducerCase3, reducerCase1 будет вызываться несколько раз, но не будут вызваны action1, action2 и reducerCase2.
Если я отправлю action2 таким же образом, как action1, он будет обрабатываться так же, как action1 и reducerCase1, многократно запуская редуктор без запуска действия.
Если после всего этого я отправлю action3 во второй раз, reducerCase1 вообще не будет запущен (как и должно быть).
Здесь меня интересуют действия GET_CATEGORIES и GET_CATS_COMPLETED:
вот журнал консоли внутри редуктора:
export default function Categories(state = initialState, action) {
switch (action.type) {
case GET_CATEGORIES:
debugger;
console.log('getting categories...');
return state.set('isLoading', true);
case GET_CATEGORIES_COMPLETED:
debugger;
console.log('setting categories...');
return state
.set('categories', fromJS(action.cats))
.set('isLoading', false);
Поскольку это происходит со всеми моими редукторами, я предполагаю, что это как-то связано с mapDispatchToProps:
const mapStateToProps = createStructuredSelector({
categories: makeSelectCategories(),
ownerId: makeSelectProfileId(),
selectedCategoryId: makeSelectSelectedCategoryId(),
isLoading: makeSelectIsLoading(),
});
const mapDispatchToProps = {
getCategories,
setCategory,
};
const withConnect = connect(
mapStateToProps,
mapDispatchToProps
);
const withReducer = injectReducer({ key: 'CategoryContainer', reducer });
const withSaga = injectSaga({ key: 'CategoryContainer', saga });
export default compose(
withSaga,
withReducer,
withConnect
)(CategoryContainer);
и вот мои действия:
export function getCategories() {
console.log('inside getCategories()');
return {
type: GET_CATEGORIES,
};
}
export function getCategoriesCompleted(cats) {
return {
type: GET_CATEGORIES_COMPLETED,
cats,
};
}
и, наконец, сага:
export default function* CategoryContainerSaga() {
yield takeLatest(GET_CATEGORIES, getCategories);
}
function* getCategories() {
try {
const plidParam = yield call(getPlParam);
const profileId = yield call(getProfileId);
const url = getUrl();
const { categories2, playlist } = yield call(
getCatsRequest,
url,
profileId,
plidParam
);
yield put(getCategoriesCompleted(categories2));
if (playlist) yield put(setPlaylist(playlist));
} catch (error) {
yield put(getCategoriesCompleted([]));
yield put(setError(error.message));
}
}