Search code examples
node.jsreactjsexpressreduxreact-redux

MERN Redux TypeError: conversations.map is not a function redux


I have a component that needs to access state in a reducer. When I call the useSelector to get the state, at first its an empty object, then about 3 seconds later, BAM data comes in.

I want to run a .find or .map on this data in the component to see if some values match.

The page loads for about 3 seconds, then bam, TypeError: conversations.map is not a function

Here is my component below

import React, { useState, useContext} from 'react';
import { TabContent, TabPane } from 'reactstrap';
import { useSelector } from 'react-redux';
import ChatContentHeader from './ChatContentHeader';
import MessageTextArea from './MessageTextArea';
import ChatContentBody from './ChatContentBody';
import { ChatContext } from '../../../context/Context';

const ChatContent = () => {
  
  const { threads, activeThreadId, selContact, activeConversationId, setSelContact } = useContext(ChatContext);
  const thread = threads.find(({ id }) => id === activeThreadId); 
  const [isOpenThreadInfo, setIsOpenThreadInfo] = useState(false);
  const conversationCreate = useSelector((state) => state.conversationCreate)
  const conversations = useSelector((state) => state.conversations)


  
  const conversation = conversations.map((c) => console.log(c)) <---- this is getting error .map is not a function... same thing if try .find instead.
// this is crashing the app the second the data hits the component.
//If I comment this out, and console.log(conversations) I see the data just fine

  console.log(conversations)
 
  return (
   
    <TabContent className="card-chat-content fs--1 position-relative">
      <TabPane className="card-chat-pane active">
        <ChatContentHeader thread={thread} setIsOpenThreadInfo={setIsOpenThreadInfo} />
        <ChatContentBody thread={thread} isOpenThreadInfo={isOpenThreadInfo} />
      </TabPane>

      <MessageTextArea thread={thread} selContact={selContact} />
    </TabContent>
    
  );
};

export default ChatContent;

Here is the reducer

const LIST_CONVERSATIONS = 'list_conversations';


export default function (state = [ ], action) {
    switch (action.type) {

        case LIST_CONVERSATIONS:
            return action.payload  
            default:
            return state;
    }
}

Here is the action creator

export const listConversations = () => {
        return async function(dispatch) {
                await 
                axios({
                 url:"http://localhost:5000/conversations",
                 method:"GET", 
                 withCredentials: true
                 }).then( res => dispatch({type: LIST_CONVERSATIONS, payload: res}))
                 }
         }

Here is my backend function being called by action creator

const listConversations = async (req, res) => {
     const { subActServiceSid, subActAuthToken, convoServiceSid} = req.user
     const subClient = require('twilio')(subActServiceSid, subActAuthToken)
     const allConversationSids = []
     const allParticipantSids = []
     const allMessages = []
     await subClient.conversations.services(convoServiceSid)
     .conversations
     .list()
     .then(conversations => conversations.forEach(c => allConversationSids.push(c.sid))); 
     await Promise.all(allConversationSids.map( async (convo) => {
        await subClient.conversations.services(convoServiceSid)
        .conversations(convo)
        .participants
        .list({limit: 20})
        .then(participants => participants.forEach(p => allParticipantSids.push({"conversationSid": p.conversationSid, "participantSid": p.sid, "messagesId": p.conversationSid}) ));
     }))
     res.json(allParticipantSids)
    
    }

Solution

  • It works before the API results come back, which means that your initial state [] is fine, and the state which is being set by your action is something which is not an array. If it's not an array then it won't have functions .map and .find.

    Your problem is right here:

    then( res => dispatch({type: LIST_CONVERSATIONS, payload: res}))
    

    You are dispatching the entire axios response object in your action payload. That response object is not an array. The array that you want should be on the.data property of the response.

    then( res => dispatch({type: LIST_CONVERSATIONS, payload: res.data}))