Search code examples
reactjsuse-effect

React update list isn't a function


I would like to update the chatList, when the user presses a button the method newChat() is called and an API is invoked. This API sends back all the old chats + the new chats. But when I call this I get an error that TypeError: chatList.map is not a function. Is the error because I am adding back the complete chats? How can I make chatList update itself when I pressed the button, I tried something once with the UseEffect, however this did not work either.

So how can I make that when the button is pressed the old and the new chats are displayed in the list.

import React, { useState, useEffect } from "react";
import { GroupchatCard } from "./GroupchatCard";
import ChatEmpty from "./ChatEmpty";
import axios from "axios";
function ChatSide() {


  const [lengthChats, setLengthChats] = useState(0);
  const [isChatEmpty, setIsChatEmpty] = useState(true);
  const [chatList, setChatList] = useState([]);

  const [messagesnumber, setMessagesnumber] = useState("");
  const getMessagesnumber = () => {
    var zuffalszahl = Math.floor(Math.random() * 5);
    setMessagesnumber(zuffalszahl);
  };
  // This should load all the chats inital at the beginning
  useEffect(() => {
 
     getChats();
    
  }, []);

   const getChats = () => {
    axios
    .get("http://localhost:4000/chat/rooms", { } )
    .then((res) => {
      if (res.status === 200) {
        if(res.data.length >= 1) {
          setLengthChats(res.data.length)
          setChatList(...chatList, res.data)
          setIsChatEmpty(false);
          localStorage.removeItem('chatList')
          localStorage.setItem('chatList', JSON.stringify(res.data))
        }
        
      }
    })
    .catch((error) => {
      console.log(error);
    });
   }
   
  // If user clicked on the plus add all chats again to the list
 useEffect(() => {
 
    // Check if some chats are saved in the local storage
     if(lengthChats > 1) {
     console.log("working only if the button is clicked")
     }
    
  }, [lengthChats]);
  /**
   * Gernate a new Chat
   */
  const newChat = () => {
    console.log(lengthChats)
    if (lengthChats < 3) {
      axios
      .get("http://localhost:4000/chat/add", { } )
      .then((res) => {
        if (res.status === 200) {
          console.log(res.data)
          if(res.data.length >= 1) {

            setLengthChats(res.data.length)
            setChatList(...chatList, res.data)
            setIsChatEmpty(false);
            localStorage.removeItem('chatList')
            localStorage.setItem('chatList', JSON.stringify(res.data))
          }
          
        }
      })
      .catch((error) => {
        console.log(error);
      });
    }
    else {
      window.alert("Du kannst nur maximal drei Chats offen haben");
    }
  }

  window.addEventListener("load", getMessagesnumber);
  return (
    <div>
      <div class="row m-1 mb-5 h-100" style={{ alignItems: "center" }}>
        <div class="col-md-11" >
          Chats
            </div>
        <div class="col-md-1" onClick={newChat}>
          <i class="fas fa-plus-circle" style={{ fontSize: "2rem", color: "#5869ff" }}></i>
        </div>
      </div>
      <div class="row mb-2">

      </div>
      <div class="row">
        <div class="col-md-12">
          {
            isChatEmpty === true &&
            <ChatEmpty></ChatEmpty>
          }
          {
            isChatEmpty === false &&
            <div className="list-group">

              {chatList.map((d, i) => (
                <GroupchatCard messagesnumber={messagesnumber} chatname={d.chatname} chatid={d.id} chatimage={d.image} />
              ))
              }

            </div>
          }


        </div>
      </div>
    </div>
  );
}

export default ChatSide;

I found out that it is because of this useEffect. But how else could I say when the component is loaded for the first time do this inital commit so we have all the values and if the user clicks a button don't consider this useEffect anymore.

  useEffect(() => {
 
     getChats();
    
  }, []);

Solution

  • You need to set the chatlist to be an array:

    setChatList(prevList => [...prevList, ...res.data])
    

    It accepts a function will pass the current chats, and now you just have to wrap with [].

    setChatList(...chatList, res.data)  // This fails
    

    This passes each item in the array as parameter (the ...). So the first item will be selected, since the first parameter will be set to the value. This not an array (only the item), so .map fails