React axios get request without resulting in infinite loop using useEffect

Viewed 2239

I'm getting an infinite loop with this axios get request, but when I try putting messages or setMessages as the value inside of the [] at the end of useEffect it takes 2 clicks of the button to update the screen or it results in an error saying insufficient network resources. So what should I put instead?

function CommentsPage() {
  const { user } = useAuth0();

  const [query, setQuery] = useState("");
  const [messages, setMessages] = useState([]);

  //fetches data from mongodb for comments
  useEffect(() => {
    fetchComments();
  }, [messages]);

  //calls backend to fetch data from the messages collection on mongodb
  async function fetchComments() {
    const response = await axios.get("http://localhost:5000/messages");

    setMessages(response.data);
  }

  // handles when a user types in the comment bar
  function handleOnInputChange(event) {
    // current value of what user is typing
    const { value } = event.target;

    setQuery(value);
  }

  // handles when a user posts a comment
  function postComment(comment, user) {
    const newMessage = {
      username: user.name,
      content: comment,
    };

    // calls backend to send a post request to insert a new document into the collection
    axios
      .post("http://localhost:5000/messages", newMessage)
      .then((res) => console.log(res.data));

    setQuery("");
    fetchComments();
  }

  // handles when a user deletes a comment
  function deleteComment(id) {
    axios
      .delete("http://localhost:5000/messages/delete/" + id)
      .then((res) => console.log(res.data));

    fetchComments();

    setMessages(messages.filter((message) => message.id !== id));
  }

  // handles when a user updates a comment
  function updateComment(id) {
    // calls a pop up that allows user to input their new comment
    const editedContent = prompt("please enter new message");

    const newMessage = {
      username: user.name,
      content: editedContent,
    };

    axios
      .put("http://localhost:5000/messages/update/" + id, newMessage)
      .then((res) => console.log(res.data));

    fetchComments();
  }

  console.log(messages);

  return (
    <Container>
      <CommentsContainer>
        {messages.length ? (
          messages.map((message) => {
            {/* if the usernames match then a user comment is returned
            otherwise an other comment will be returned */}
            return message.username === user.name ? (
              <UserComment
                key={message._id}
                username={message.username}
                content={message.content}
                deleteComment={deleteComment}
                id={message._id}
                updateComment={updateComment}
              />
            ) : (
              <OtherComment
                key={message._id}
                username={message.username}
                content={message.content}
              />
            );
          })
        ) : (
          <div>There are no comments. Make one!</div>
        )}
      </CommentsContainer>
      <CommentBarStyle htmlFor="search-input">
        <input
          name="commentBar"
          type="text"
          value={query}
          id="search-input"
          placeholder="Write a comment..."
          onChange={handleOnInputChange}
        />
        <AddIcon
          className="fas fa-plus"
          onClick={() => postComment(query, user)}
        />
      </CommentBarStyle>
    </Container>
  );
}

export default CommentsPage;
2 Answers

Your useEffect will run every time when messages state is change. Change your useEffect like this:

useEffect(() => {
    fetchComments();
},[null]);

You are updating the 'messages' state inside the fetchComments... so everytime this state is updated your useEffect will be called because it has the messages as a dependency.

  useEffect(() => {
    fetchComments();
  }, [messages]);

  //calls backend to fetch data from the messages collection on mongodb
  async function fetchComments() {
    const response = await axios.get("http://localhost:5000/messages");

    setMessages(response.data);
  }

Try this:

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

If you do it in that way, your component will call the fetchComments in the mount phase instead of calling it everytime that messages state changes.

Related