redux 如何在不重新加载页面的情况下在帖子显示上点赞?

cbwuti44  于 2023-06-23  发布在  其他
关注(0)|答案(3)|浏览(122)

不明白如何在不刷新页面的情况下查看帖子上的喜欢。将请求发送到后端并接收响应似乎很好,帖子得到喜欢和不喜欢,但要看到喜欢,我需要重新加载页面,我很难理解现在该怎么做。还在学习一切是如何运作的。
下面是后端代码

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');
    }
  }

这里是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;

这是邮政编码

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>
    </>
  )
}

下面是帖子的样子post in console
我能尝试的最好方法是将likedByarray.length放入useState中,但它总是未定义的,如果它由于某种原因设法出现,它将无法正常工作

dgiusagp

dgiusagp1#

95%的时间都能正常工作
其他5%我喜欢超越,并添加或删除一个额外的喜欢或两个(如果我继续点击),直到它停止,并认为一秒钟,然后回到正确的价值

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 >
mzmfm0qo

mzmfm0qo2#

如果你想创建一个高性能的社交网络,你可以遵循许多平台使用的通用方法。当用户执行一个动作时,比如喜欢一个帖子,你可以向后端发送一个请求,并使用React.useState来更新liked show的值,如果你愿意的话,还可以添加一些样式。这样,执行该操作的用户将被通知其操作。此外,当另一个用户登录时,他们将能够看到其他用户所做的喜欢,因为更新的数据被提取。Twitter有效地实现了这种行为。
存在另一种方法,Socket.io当另一个用户执行诸如喜欢帖子之类的动作时,可以利用www.example.com向用户发送实时更新。当用户喜欢某个帖子时,您可以通过Socket.io向服务器发送事件,然后服务器将此信息广播给所有其他连接的用户。因此,其他用户将收到此实时更新,并能够看到其他用户执行的操作,而无需刷新页面。check the documentation of socket.io

myzjeezk

myzjeezk3#

如上所述,将API调用与useState一起使用。下面是useMutation的一个例子(来自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);
  };

而“like”按钮可以是这样的:

<button
          onClick={onLikedHandler}
          disabled={!user_id}
          className="material-symbols-outlined"
        >
          {!liked ? "favorite" : "heart_check"}
          <span className={classes.likes_num}>{likes}</span>
        </button>

相关问题