qdog
qdog

Reputation: 151

Redux reducer running multiple times when unused action is first dispatched

Update: I'm using React-Boilerplate, unmodified from the original except in the containers / components. The reducers run multiple times, sometimes more than twice, when a new action is dispatched for the first time, but not when the same action is dispatched subsequently. The actions themselves are not fired on the repeated reducer calls, but the state is updated and re-renders the component.

For example: If I dispatch action1 which updates reducerCase1, but not action2 which updates reducerCase2, action1 will run once, and reducerCase1 will run twice. action2 and reducerCase2 will not run. If I then dispatch action3 which updates reducerCase3, reducerCase1 will be called multiple times, but action1, action2, and reducerCase2 will not be called.

If I dispatch action2 in the same manner as action1, it will be treated the same way as action1 and reducerCase1, running the reducer multiple times without firing the action.

If after all this I dispatch action3 a second time, reducerCase1 will not be run at all (as should be the case).

Here I'm interested GET_CATEGORIES and GET_CATS_COMPLETED actions:

enter image description here

here is the console log inside the reducer:

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

enter image description here

Since this is happening with all of my reducers, I assume it has something to do with 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);

and here are my actions:

export function getCategories() {
  console.log('inside getCategories()');
  return {
    type: GET_CATEGORIES,
  };
}

export function getCategoriesCompleted(cats) {
  return {
    type: GET_CATEGORIES_COMPLETED,
    cats,
  };
}

and finally the saga:

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

Upvotes: 2

Views: 4355

Answers (1)

qdog
qdog

Reputation: 151

Thanks to lecstor's comment, I was able to determine that this is expected behavior of Redux devtools.

Upvotes: 2

Related Questions