ReactJS中如何关联帖子与点赞数?控制台返回undefined求助
问题描述
我有一个Laravel API用于从数据库获取帖子数据,另一个API用于获取点赞数据。现在我想在React组件中展示帖子,并显示每个帖子对应的点赞数量。我尝试了如下代码,但控制台输出值为undefined,请求帮助。
Community 组件代码
import React, { useState, useEffect } from 'react'; import { useNavigate } from "react-router-dom"; import Menubar from "../../components/menubar/Menubar" import PostSkeleton from '../../components/skeleton/PostSkeleton'; import PostWidget from '../../components/widgets/PostWidget'; import axios from 'axios'; function Community() { let navigate = useNavigate(); const [posts, setPosts] = useState([]); const [upvotes, setUpvotes] = useState([]); const [limit, setLimit] = useState(4); const [isLoading, setIsLoading] = useState(true); const [loading, setLoading] = useState(false); function handleClick() { navigate("/add-post"); } useEffect(() => { let isMounted = true; axios.get(`/api/posts?page=1&limit=` + limit).then(res => { if (isMounted) { if (res.data.status === 200) { setPosts(res.data.posts); setIsLoading(false); setLoading(false); } } }); return () => { isMounted = false }; }, [limit]); const loadMore = () => { setLimit(limit + 4) setLoading(true); } useEffect(() => { let isMounted = true; axios.get(`/api/upvotes`).then(res => { if (isMounted) { if (res.data.status === 200) { setUpvotes(res.data.upvotes); } } }); return () => { isMounted = false }; }, []); return ( <div> <Menubar /> <div className="appHeader bg-primary text-light"> <div className="left"> <a onClick={() => navigate(-1)} className="headerButton goBack"> <i className="fi fi-rr-angle-left"></i> </a> </div> <div className="pageTitle">Community</div> <div className="right"> <a href="#" className="headerButton"> <i className="fi fi-rr-paper-plane"></i> </a> </div> </div> <div id="appCapsule"> <div className="section"> <div className="post-input mt-3"> <form> <a href="profile.php" className="btn btn-icon btn-secondary rounded mr-1" > <img src="assets/img/sample/avatar/avatar4.jpg" alt="avatar" className="avatar imaged rounded" /> </a> <div className="form-group boxed"> <div className="input-wrapper"> <input type="text" onClick={handleClick} className="form-control" placeholder="Tell the World Something" /> </div> </div> </form> </div> </div> <div className="section mt-2 mb-3"> {isLoading && <PostSkeleton cards={4} />} {posts.map((post) => (<PostWidget post={post} upvotes={upvotes.find(upvotes =>posts.postid === upvotes.post_id)} key={post.postid} />))} <div className="text-center"> <a onClick={loadMore} className={limit <= posts.length ? 'btn btn-text-primary mr-1' : 'btn btn-text-primary mr-1 disabled'} >{loading ? <> <span className="spinner-border spinner-border-sm mr-05" role="status" aria-hidden="true"></span>Loading More</> : <> {limit <= posts.length ? <>Load More <i className="fi fi-rr-angle-small-right"></i></> : 'All Posts Loaded'} </>}</a> </div> </div> </div> </div> ); } export default Community;
PostWidget 组件代码
import React, { useState, useEffect } from 'react'; import { Link } from "react-router-dom"; import { LazyLoadImage } from "react-lazy-load-image-component"; import axios from 'axios'; import toast, { Toaster } from 'react-hot-toast'; const PostWidget = ( {post, upvotes}) => { console.warn(upvotes && upvotes.length) const formatDate = (dateString) => { const options = { year: "numeric", month: "long", day: "numeric" } return new Date(dateString).toLocaleDateString(undefined, options) } return ( <div> <Toaster /> <div className="comment-block mb-3 pb-1"> <div className="comment-header"> <div className="avatar"> <img src="assets/img/sample/avatar/avatar1.jpg" alt="avatar" className="imaged w32 rounded mr-1" /> </div> <div className="comment-other"> <h4 className="title">{post.user && post.user.firstname} {post.user && post.user.lastname}</h4> <span className="time">{formatDate(post.created_at)}</span> </div> </div> <div className="item"> <div className="in"> <div className="post-image mt-1"> <Link to={"/post-details/" + post.postid}> <LazyLoadImage src={`http://localhost:8000/${post.postimage}`} alt="avatar" className="imaged w-100" /> </Link> <Link to={"/fruit-details/" + post.fruit.id}> <div className="chip mt-1 mr-1"> <span className="chip-label">{post.fruit.name}</span> </div> </Link> </div> <Link to={"/post-details/" + post.postid}> <div className="text mt-1"> {post.postcontent} </div> </Link> <div className="comment-footer"> <a href="" className="comment-button"> <i className="fi fi-rr-check"></i> Upvote (5) </a> <a href="" className="comment-button"> <i className="fi fi-rr-arrow-down"></i> Downvote </a> <Link to={"/post-details/" + post.postid} className="comment-button" > <i className="fi fi-rr-comment"></i> Comment </Link> </div> </div> </div> </div> </div> ); } export default PostWidget;
解决方案
1. 修复匹配逻辑错误
在Community组件的posts.map循环中,你错误地引用了整个帖子数组posts的postid属性,而应该使用当前迭代的单个帖子对象post的postid。同时,find方法只会返回第一个匹配的点赞记录,无法统计总点赞数,应该改用filter过滤所有对应帖子的点赞后取数组长度:
// 原错误代码 upvotes.find(upvotes => posts.postid === upvotes.post_id) // 修改后代码 upvotes.filter(upvote => upvote.post_id === post.postid).length
2. 调整传递给PostWidget的参数
将Community组件中的map部分修改为直接传递点赞数量:
{posts.map((post) => ( <PostWidget post={post} upvoteCount={upvotes.filter(upvote => upvote.post_id === post.postid).length} key={post.postid} /> ))}
3. 更新PostWidget接收并显示点赞数
修改PostWidget组件的参数接收和点赞数展示部分:
const PostWidget = ({ post, upvoteCount }) => { // 保留原有代码 return ( // 保留原有结构 <div className="comment-footer"> <a href="" className="comment-button"> <i className="fi fi-rr-check"></i> Upvote ({upvoteCount}) </a> {/* 保留其他按钮代码 */} </div> // 保留其他结构 ); }
4. 处理异步加载的初始状态
由于帖子和点赞数据都是异步请求,初始时upvotes为空数组,filter后长度为0,upvoteCount会默认显示0,不会出现undefined。如果需要更严谨,可添加默认值:
upvoteCount={upvotes?.filter(upvote => upvote.post_id === post.postid).length || 0}
内容的提问来源于stack exchange,提问作者Oscar John
相关产品推荐
相关产品推荐

