Search code examples
reduxreact-reduxrtk-querynormalizrcreateentityadapter

Redux Toolkit: How to normalize group chat messages?


I have an array of messages returned from the server where each chat message has the following format:

export interface ChatMessage {
  _id: string | number;
  text: string;
  createdAt: Date | number;
  user: ChatUser;
  groupId: number;
}

interface ChatUser {
  _id: string | number;
  name: string;
}

I want to normalize the list of messages by groupId AND then by message Id nested inside to achieve something like this:
const messages = {
  ids: ['group1', 'group2'],
  entities: {
   group1: {
      ids: ['msg1', 'msg2'],
      msg1: {},
      msg2: {},
    },
   group2: {
      ids: ['msg3', 'msg4'],
      msg3: {},
      msg4: {},
   },
};

How can I achieve that with createEntityAdapter or with the normalizr library?


Solution

  • I think the heavy lifting can be done with selectors if the state is well designed. I'd try something like this:

    const initialState = {
        messages: {}, // messages by id
    
        // map groupIds to an object with messageIds and their createdAt timestamp
        groups: {
            /* Example:
            groupId1: { messageId1: '2022-05-23', messageId2: '2022-05-22'}
            */
        },
        users: {}, // users by id
    };
    
    chatMessagesFromServer.forEach(message => {
        const { user } = message;
    
        // Save message by id, but only with a reference to the userId
        const normalizedMessage = { ...message, userId: user._id };
        delete normalizedMessage.user;
        state.messages[message._id] = normalizedMessage;
    
        // Associate groups with messages
        if (state.groups[message.groupId]) {
            state.groups[message.groupId][message.id] = message.createdAt;
        } else {
            state.groups[message.groupId] = { [message.id]: message.createdAt };
        }
    
        // Save user by id
        state.users[user._id] = user;
    });
    

    Retrieving all messages in one group in chronological order for example is a text book example of a memoized selector accessing state.groups to get the message ids, then enriching that list of ids with the message data from state.messages.