Issues loading comments in Realtime

Viewed 138

I have a social media app that I would like to function like facebook, where you comment and the comment is loaded in real-time with out having to refresh your browser to display the comment. I am able to send data from React to backend server and I am able to get that data with a axios http request, but I have to refresh the browser to see the comment displayed. I am also see the comment display more then once. I am not getting any errors but the comment is not unique to the post, as it is an array that loads the posts. Did I make a mistake in my code?

Here is the front end code.

import React, { useState, useEffect } from "react";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import axios from "axios";
import "./css/sharewall.css";

const ComponentName = () => {
  const [posts, setPosts] = useState([]);
  const [comment, setComment] = useState("");
  const [id, setId] = useState("");

  const loadData = async () => {
    try {
      let res = await axios.get(`http://localhost:5000/getall`);
      setPosts(res.data);
    } catch (error) {
      console.log(error);
    }
  };

  function makeRequest(e) {
    e.preventDefault();
    axios({
      method: "POST",
      url: "http://localhost:5000/postinput",
      data: {
        comment: comment,
      },
    }).then((res) => {
      setComment(res.data.comment);
      console.log(res.data);
    });
  }

  const loadComment = async () => {
    try {
      let res = await axios.post("http://localhost:5000/postinput");
      setComment(res.data.comment._id);
      console.log(res.data.comment._id)
    } catch (error) {
      console.log(error);
    }
  };

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

  return (
    <div className="compoentclass">
      <Container className="mt-5 ml-auto mr-auto">
        <div className="text-center">
          {posts.map((post, index) => (
            <div>
              <Card className="">
                <Card.Img alt="" src={post.url} />
                <Card.ImgOverlay className="overlay">
                  <Card.Title className="text-center mt-5">
                    <Card.Text className="cardStyle text-light">
                      {post.body}
                    </Card.Text>
                  </Card.Title>
                </Card.ImgOverlay>
              </Card>
              {posts.map((post, index) => (
              <div><Card.Text>{post.comment}</Card.Text></div>
              ))}
              <textarea
              className="comment text-center mt-3 mb-3"
              onChange={e => setComment(e.target.value)}
              value={comment}
              name={"comment"}
              type={"text"}
            />
              <div className="d-flex justify-content-start mt-n3 mb-4">
                <Button
                  className="shareButton"
                  variant="secondary"
                  onClick={makeRequest}
                  onChange={loadComment}
                >
                  Comment
                </Button>
              </div>
            </div>
          ))}
        </div>
      </Container>
    </div>
  );
};

export default ComponentName;

Here is the render from the comments, the comments double or tripple. enter image description here

2 Answers

In order for other users (the user that posts a comment should be easily able to see the comment immediately) to see the comments real-time, you must implement some sort of "listener" to the server/database to listen for new comments. Otherwise, how should my browser know that YOU posted a comment just now? Check out socket.io, it is quite easy to implement.

I've added some additions to your code, see comments. First, it seems you can use useEffect to rerender your comments every time you will click the "comment" button. To handle updates you can create a new state as I did.
Probably you are having troubles with multi comments because your posts array contains more than one element inside. Inside render it maps through all posts array and displays every element.
Also, would be better if you will recreate your code inside codesandbox.io or similar.

import React, { useState, useEffect } from "react";
import Container from "react-bootstrap/Container";
import Card from "react-bootstrap/Card";
import Button from "react-bootstrap/Button";
import axios from "axios";
import "./css/sharewall.css";

const ComponentName = () => {
  const [posts, setPosts] = useState([]);
  const [comment, setComment] = useState("");
  const [id, setId] = useState("");

  //state for resending load request
  const [isCommentFetched, setCommentFetched] = useState(false);

  const loadData = async () => {
    try {
      let res = await axios.get(`http://localhost:5000/getall`);
      setPosts(res.data);
    } catch (error) {
      console.log(error);
    }
  };

  function makeRequest(e) {
    e.preventDefault();
    axios({
      method: "POST",
      url: "http://localhost:5000/postinput",
      data: {
        comment: comment,
      },
    }).then((res) => {
      setComment(res.data.comment);
      setCommentFetched(true)
      console.log(res.data);
    })
    //don't forget to catch errors
    .catch((err)=>{
    console.log(err)
    })
  }

  const loadComment = async () => {
    try {
      let res = await axios.post("http://localhost:5000/postinput");
      setComment(res.data.comment._id);
      console.log(res.data.comment._id);
    } catch (error) {
      console.log(error);
    }
  };

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

//hook fires whenever your isCommentFetched state updating. 
  useEffect(() => {
      // if isCommentFetched true, it will send request for get new posts, and will update your comments in render.
    if(isCommentFetched){
        loadData();
    }
  }, [isCommentFetched]);

  return (
    <div className="compoentclass">
      <Container className="mt-5 ml-auto mr-auto">
        <div className="text-center">
          {posts.map((post, index) => (
            <div>
              <Card className="">
                <Card.Img alt="" src={post.url} />
                <Card.ImgOverlay className="overlay">
                  <Card.Title className="text-center mt-5">
                    <Card.Text className="cardStyle text-light">
                      {post.body}
                    </Card.Text>
                  </Card.Title>
                </Card.ImgOverlay>
              </Card>
              {posts.map((post, index) => (
                <div>
                  <Card.Text>{post.comment}</Card.Text>
                </div>
              ))}
              <textarea
                className="comment text-center mt-3 mb-3"
                onChange={(e) => setComment(e.target.value)}
                value={comment}
                name={"comment"}
                type={"text"}
              />
              <div className="d-flex justify-content-start mt-n3 mb-4">
                <Button
                  className="shareButton"
                  variant="secondary"
                  onClick={makeRequest}
                  onChange={loadComment}
                >
                  Comment
                </Button>
              </div>
            </div>
          ))}
        </div>
      </Container>
    </div>
  );
};

export default ComponentName;
Related