You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

新注册用户访问文章时显示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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 23:45:04