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)
}
CodePudding user response:
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}))