新注册用户访问文章时显示Already upvoted问题求助
问题
这是一个全栈博客网站,仅注册用户可进行点赞和评论。按预期新注册用户应能对文章点赞,但实际创建新用户后,点赞按钮始终显示“Already upvoted”,而非“Upvote”。已尝试调整useEffect内的loadArticleInfo逻辑,但问题依旧。
前端ArticlePage组件代码
import { useState, useEffect } from 'react'; import {useParams} from 'react-router-dom'; import axios from 'axios'; import NotFoundPage from './NotFoundPage'; import CommentsList from '../components/CommentsList'; import articles from './article-content'; import useUser from '../hooks/useUser'; import AddCommentForm from '../components/AddCommentForm'; const ArticlePage = () => { // 从当前路由中提取articleId参数 const {articleId} = useParams(); const [articleInfo, setArticleInfo] = useState({ upvotes: 0, comments: [], canUpvote: false }); const { canUpvote } = articleInfo; const { user, isLoading } = useUser(); useEffect(() => { const loadArticleInfo = async () => { const token = user && await user.getIdToken(); const headers = token ? { authToken: token } : {}; const response = await axios.get(`/api/articles/${articleId}`,{headers}); const newArticleInfo = response.data; setArticleInfo(newArticleInfo) }; if(isLoading){ loadArticleInfo(); } }, [isLoading, user, articleId]); // 查找匹配articleId的文章 const article = articles.find(article => article.name === articleId); const addUpvote = async () => { const token = user && await user.getIdToken(); const headers = token ? { authToken: token } : {}; const response = await axios.put(`/api/articles/${articleId}/upvote`, null, {headers}); const updatedArticleInfo = response.data; setArticleInfo(updatedArticleInfo); }; if(!article) { return <NotFoundPage />; } return ( <div> <h1>{article.title}</h1> <div className="upvotes-section"> { user ? ( <button onClick={addUpvote}> {canUpvote ? 'Upvote' : 'Already Upvoted'} </button> ) : ( <button>Log in to upvote</button> )} <p> This article has {articleInfo.upvotes} upvote(s).</p> </div> {/* 遍历文章内容生成段落 */} {article.content.map((paragraph, i) => ( <p key={i}>{paragraph}</p> ))} {user ?<AddCommentForm articleName={articleId} onArticleUpdate={updatedArticle => setArticleInfo(updatedArticle)} /> :<button>Log in to add a comment</button> } <CommentsList comments={articleInfo.comments} /> </div> ); }; export default ArticlePage;
后端server.js代码
import express from 'express'; import fs from 'fs' import admin from 'firebase-admin'; import {db, connectDb} from './db.js'; const credentials = JSON.parse( fs.readFileSync('./credentials.json') ); admin.initializeApp({ credential: admin.credential.cert(credentials) }); const app = express(); app.use(express.json()); app.use(async(req, res, next) => { const {authToken} = req.headers; if(authToken) { try{ req.user = await admin.auth().verifyIdToken(authToken); }catch(err) { return res.sendStatus(400); } } req.user = req.user || {}; next(); }); app.get('/api/articles/:name', async (req, res) => { const {name} = req.params; const {uid} = req.user; const article = await db.collection('articles').findOne({ name }); if(article) { const upvotesIds = article.upvotesIds || []; article.canUpvote = uid && !upvotesIds.includes(uid); res.json(article); }else{ res.status(404).send('Article not found!'); } }); app.use(( req, res, next) => { if(req.user) { next(); }else{ res.sendStatus(401); } }); app.put('/api/articles/:name/upvote',async (req, res) => { const {name} = req.params; const {uid} = req.user; const article = await db.collection('articles').findOne({ name }); if(article) { const upvotesIds = article.upvotesIds || []; const canUpvote = uid && !upvotesIds.includes(uid); if(canUpvote) { await db.collection('articles').updateOne({ name }, { '$inc': { 'upvotes': 1 }, '$push': { 'upvotesIds': uid } }); } const updatedArticle = await db.collection('articles').findOne({ name }); res.json(updatedArticle); } else{ res.status(404).send('The Article does not found!'); } }); app.post('/api/articles/:name/comments', async (req, res) => { const {name} = req.params; const { comment } = req.body; const {email} = req.user; await db.collection('articles').updateOne({ name }, { '$push': { 'comments': { postedBy: email, comment } } }); const article = await db.collection('articles').findOne({ name }); if(article) { res.json(article); }else{ res.status(404).send('The Article not found!'); } }); connectDb(() => { console.log('Connected to Database!'); app.listen(8000, () => { console.log('Server is listening on port 8000!'); }); });
问题分析与解决方案
核心问题
前端useEffect的触发逻辑错误:当前仅在isLoading为true时请求文章数据,但isLoading是用户加载过程中的临时状态,加载完成后会变为false,导致新用户登录后无法触发数据请求,articleInfo停留在初始的canUpvote: false状态,按钮因此一直显示“Already upvoted”。
修复步骤
1. 修正useEffect触发条件
将useEffect内的判断条件改为if(user && !isLoading),确保用户加载完成后才请求文章数据:
useEffect(() => { const loadArticleInfo = async () => { const token = user && await user.getIdToken(); const headers = token ? { authToken: token } : {}; const response = await axios.get(`/api/articles/${articleId}`,{headers}); const newArticleInfo = response.data; setArticleInfo(newArticleInfo) }; // 用户加载完成后再请求数据 if(user && !isLoading){ loadArticleInfo(); } }, [isLoading, user, articleId]);
2. 优化初始状态(可选)
将articleInfo的初始canUpvote设为null,避免未加载完成时显示错误状态,同时在渲染按钮时处理该状态:
const [articleInfo, setArticleInfo] = useState({ upvotes: 0, comments: [], canUpvote: null }); // 按钮渲染逻辑 <button onClick={addUpvote}> {canUpvote === null ? 'Loading...' : canUpvote ? 'Upvote' : 'Already Upvoted'} </button>
3. 后端验证(辅助排查)
确认后端/api/articles/:name接口中:
uid能正确从Firebase Token解析,无空值或错误upvotesIds数组未错误包含新用户的UID
验证效果
修改后,新用户登录后会自动请求文章数据,后端根据用户UID返回正确的canUpvote状态,前端按钮将正常显示“Upvote”。
内容的提问来源于stack exchange,提问作者beginner
相关产品推荐
相关产品推荐

