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?
Advertisement
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.