skip to Main Content

Dont understand how to see likes on posts without refreshing the page. Sending the request to backend and receiving the response seems fine, posts getting liked and unliked, but to see the likes i need to reload the page and im having a hard time understanding what to do now. Still learning how everything works.

here is the backend code

async likePost(req, res) {
    try {
      const post = await Post.findOne({ where: { id: req.params.id } });
      const alreadyLiked = await Likes.findOne({ where: { post_id: post.id, user_id: req.session.user.id } });
      if (!alreadyLiked) {
        const like = await Likes.create({ post_id: post.id, user_id: req.session.user.id });
        console.log('post liked');
        res.json({ like });
      } else {
        await Likes.destroy({ where: { post_id: post.id, user_id: req.session.user.id } });
        console.log('post unliked');
        res.sendStatus(200);
      }
    } catch (error) {
      console.log(error);
      res.sendStatus(500).send('Server Error');
    }
  }

here is the likeSlice

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"
import axios from "axios";

const initialState = {
  likes: []
}

export const upvotePost = createAsyncThunk(
  'likes/upvotePost',
  async (id, { rejectWithValue, dispatch }) => {
    try {
      const response = await axios.post(`/posts/${id}/likes`);
      dispatch(addLike(response.data.like))
    } catch (error) {
      rejectWithValue(error.message);
    }
  }
)

export const likeSlice = createSlice({
  name: 'likes',
  initialState,
  reducers: {
    addLike: (state, action) => {
      state.likes.push(action.payload);
    }
  }
})

const { addLike } = likeSlice.actions;
export default likeSlice.reducer;

here is the post code

const OnePost = () => {

  const post = useSelector(state => state.post.post_id);
  const user = useSelector(state => state.auth.user);
  console.log('post', post);
  
 

  const { id } = useParams();
  const dispatch = useDispatch();


  const num = post.likedBy?.length
  console.log('num', num);
  const [like, setLike] = useState(num)
  console.log('like--->', like);
  


  useEffect(() => {
    dispatch(getOnePost(id))
  }, [like])

  const navigate = useNavigate();

  const openUserPageHandler = () => {
    dispatch(getOneUser(post.user_id))
  }


  const likePostHandler = () => {
    // if (post.likedBy?.map(el => el.Likes.user_id == user.id)) {
    //   setLike(prev => prev - 1)
      dispatch(upvotePost(id))
    // } else {
    //   setLike(prev => prev + 1)
    //   dispatch(upvotePost(id))
    // }
  }

  const avatar = `http://localhost:3001${post.userAvatar}`
  const createdAt = new Date(post.createdAt).toLocaleString(undefined, { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' });
  const comments = useSelector(state => state.comment.comments);

  useEffect(() => {
    dispatch(getPostComments(id));
  }, [])


  return (
    <>

      <div className='one-post__container'>
        <div className='one-post'>
          <div className='one-post__info'>
            <div className='one-post__info__head'>
              <div onClick={() => navigate(-1)}>
                <ArrowBackIcon className='one-post__back__arrow' />
              </div>
              <span className='profile__name'>
                <h3>Tweet</h3>
              </span>
            </div>
            <div className='one-post__info__mid'>
              <Link to={`/user/${post.userId}`} className='one-post__link'>
                <div className='one-post__name' onClick={openUserPageHandler}>
                  <img src={post.userAvatar ? avatar : default_avatar} alt='' className='one-post__profile__avatar' />
                  <h4 className='one-post__user__name'>{post.userName}</h4>
                </div>
              </Link>
              <div className='one-post__options'>
                <MoreHoriz />
              </div>
            </div>

            <div className='one-post__body'>{post.text}</div>
            <div className='one-post__footer'>
              <div className='one-post__time'>{createdAt}</div>
              <div className='one-post__icons'>
                <div className='one-post__icons__options'>
                  <ChatBubbleOutline fontSize='small' />
                </div>
                <div className='one-post__icons__options'>
                  <FavoriteBorderOutlined fontSize='small' onClick={likePostHandler} />
                  {like > 0 ? like : null}
                </div>
              </div>
            </div>
          </div>
        </div>
        
      </div>
    </>
  )
}

and here is how the post looks like
post in console

best i could try was putting likedBy array.length in useState but it was always undefined, and if it managed to appear for some reason it was not working properly

3

Answers


  1. Chosen as BEST ANSWER

    got it working 95% of the time

    other 5% my like goes beyond and adds or removes an extra like or two (if i keep clicking) until it stops and thinks for a second and then goes back to correct value

    const OnePost = () => {
    
      const post = useSelector(state => state.post.post_id);
      const user = useSelector(state => state.auth.user);
    
      const postLikes = post.likedBy?.length;
      const alreadyLiked = post.likedBy?.map(el => el.Likes.user_id).includes(user.id);
      const [likes, setLikes] = useState(postLikes);
      const [isLiked, setIsLiked] = useState(alreadyLiked);
    
      const { id } = useParams();
      const dispatch = useDispatch();
    
      const navigate = useNavigate();
    
      const openUserPageHandler = () => {
        dispatch(getOneUser(post.user_id))
      }
    
      const likePostHandler = () => {
        if (alreadyLiked) {
          setLikes(prev => prev - 1);
          setIsLiked(false)
          dispatch(upvotePost(id));
        } else {
          setLikes(prev => prev + 1);
          setIsLiked(true)
          dispatch(upvotePost(id))
        }
      }
    
      useEffect(() => {
        dispatch(getOnePost(id))
        console.log('likes changed');
      }, [likes])
    
      useEffect(() => {
        setLikes(postLikes)
        setIsLiked(alreadyLiked)
        console.log('set likes');
      }, [postLikes, alreadyLiked])
    
      const avatar = `http://localhost:3001${post.userAvatar}`
      const createdAt = new Date(post.createdAt).toLocaleString(undefined, { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' });
      const comments = useSelector(state => state.comment.comments);
    
      useEffect(() => {
        dispatch(getPostComments(id));
      }, [])
    
    
      return (
        <>
    
          <div className='one-post__container'>
            <div className='one-post'>
              <div className='one-post__info'>
                <div className='one-post__info__head'>
                  <div onClick={() => navigate(-1)}>
                    <ArrowBackIcon className='one-post__back__arrow' />
                  </div>
                  <span className='profile__name'>
                    <h3>Tweet</h3>
                  </span>
                </div>
                <div className='one-post__info__mid'>
                  <Link to={`/user/${post.userId}`} className='one-post__link'>
                    <div className='one-post__name' onClick={openUserPageHandler}>
                      <img src={post.userAvatar ? avatar : default_avatar} alt='' className='one-post__profile__avatar' />
                      <h4 className='one-post__user__name'>{post.userName}</h4>
                    </div>
                  </Link>
                  <div className='one-post__options'>
                    <MoreHoriz />
                  </div>
                </div>
    
                <div className='one-post__body'>{post.text}</div>
                <div className='one-post__footer'>
                  <div className='one-post__time'>{createdAt}</div>
                  <div className='one-post__icons'>
                    <div className='one-post__icons__options'>
                      <ChatBubbleOutline fontSize='small' />
                    </div>
                    <div className={isLiked ? 'heart active' : 'heart'}>
                      <FavoriteBorderOutlined fontSize='small' onClick={likePostHandler} />
                      {likes > 0 ? likes : null}
                    </div>
                  </div>
                </div>
              </div>
            </div>
            <CommentForm />
            <ul className='comments__list'>
              {comments.map((el) => (
                <li key={el.id}>
                  <Comment
                    id={el.id}
                    text={el.text}
                    createdAt={el.createdAt}
                    userId={el.User?.id}
                    userName={el.User?.name}
                    userAvatar={el.User?.avatar}
                  />
                </li>
              ))}
            </ul>
          </div >
    

  2. If you want to create a performant social network, you can follow a common approach used by many platforms. When a user performs an action, such as liking a post, you can send a request to the backend and use React.useState to update the value of liked show and add some style if you want. This way, the user who performed the action will be notified about their action. Additionally, when another user logs in, they will be able to see the likes made by other users because the updated data is fetched. Twitter implements this behavior effectively.

    there is another approach, Socket.io can be utilized to send real-time updates to users when another user performs an action, such as liking a post. When a user likes a post, you can send an event via Socket.io to the server, which will then broadcast this information to all other connected users. As a result, other users will receive this real-time update and be able to see the actions performed by other users without needing to refresh the page.
    check the documentation of socket.io

    Login or Signup to reply.
  3. As stated above, use api calls together with useState. Here’s an example with useMutation (from Tanstack Query (react query)):

    const likeCommentMutation = useMutation({
        mutationFn: (comment_id) => {
          likeComment(comment_id);
        },
        onSuccess: () => {
          setLiked((current) => !current);
          if (liked) {
            setLikes((current) => {
              return current - 1;
            });
          } else {
            setLikes((current) => {
              return current + 1;
            });
          }
        },
        onError: () => {
          //error handling
          );
        },
      });
    
      const onLikedHandler = () => {
        likeCommentMutation.mutate(reply.id);
      };
    

    And "like" button can be like this:

       <button
              onClick={onLikedHandler}
              disabled={!user_id}
              className="material-symbols-outlined"
            >
              {!liked ? "favorite" : "heart_check"}
              <span className={classes.likes_num}>{likes}</span>
            </button>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search