How to update the state in React Redux?

I’m using a reducer to set the state in Redux. My state currently looks something like this.

{
  activeConversation: "Jim"
  conversations: (7) [{…}, {…}, {…}, {…}, {…}, {…}, {…}]
  user: {id: 8, username: "josh", email: ""}
}

In my old reducer, I was just getting the conversations array and setting that but now I need access to the activeConversation string as well. Therefore, I decided to use my root reducer which combines everything so it can work correctly. Here’s my root reducer.

import { createStore, applyMiddleware, combineReducers } from "redux";
import loggerMiddleware from "redux-logger";
import thunkMiddleware from "redux-thunk";

import user from "./user";
import conversations from "./conversations";
import activeConversation from "./activeConversation";
import {
addMessageToStore,
} from "./utils/reducerFunctions";

const CLEAR_ON_LOGOUT = "CLEAR_ON_LOGOUT";
const SET_MESSAGE = "SET_MESSAGE";

export const clearOnLogout = () => {
  return {
    type: CLEAR_ON_LOGOUT
  };
};

export const setNewMessage = (message, sender) => {
  return {
    type: SET_MESSAGE,
    payload: { message, sender: sender || null },
  };
};

const appReducer = combineReducers({
  user,
  conversations,
  activeConversation
});
const rootReducer = (state, action) => {
  if (action.type === CLEAR_ON_LOGOUT) {
    state = undefined;
  } else if (action.type === SET_MESSAGE) {
    return addMessageToStore(state, action.payload);
  }
  return appReducer(state, action);
};

export default createStore(rootReducer, applyMiddleware(thunkMiddleware, loggerMiddleware));

My setNewMessage function is called which then calls addMessageToStore.

export const addMessageToStore = (state, payload) => {
  const { message, sender } = payload;
  return { ...state, conversations: state.conversations.map((convo) => {
    if (convo.id === message.conversationId) {
      const newUnread = convo.unreadMessages;
      if (state.activeConversation === convo.otherUser) {
        newUnread = (parseInt(newUnread) + 1).toString();
      }
      const newConvo = {
        ...convo,
        messages: convo.messages.concat(message),
        latestMessageText: convo.latestMessageText,
        unreadMessages: newUnread
      }
      console.log("newConvo:", {...state, conversations: newConvo});
      return {...state, conversations: newConvo};
    } else {
      return {...state, conversations: convo};
    }
  })};
};

The problem with this is that next state isn’t being updated. When it returns next state, it just shows the previous state instead of my new one. Does anyone know what’s going on?

Answer

I think you are “cutting” your state slices inefficiently. At best, a Reducer should always own their own state, and you are right now going to great lengths to have a meta-reducer for conversations to have access to the activeConversation.

Why not have a normal reducer for both?

Have a state of the shape

{
  user,
  conversations
}

where conversations has the shape

{
  items,
  active
}

That way your conversations reducer has just access to it’s own state properties – items and active – and does not need to to any gymnastics.