Next 14项目中是否仍需使用React Context实现文章收藏功能?
Next.js 14 文章收藏状态管理的优化方案
先给你明确结论:不是必须继续用React Context,但得结合Next.js 14的渲染特性和缓存策略来优化,解决重复查询的问题。下面给你几个可行的方案:
1. 保留Context,但搭配服务器端预取+缓存
如果你的组件层级比较深,需要跨多个组件传递收藏状态,继续用Context没问题,但别让客户端每次加载都查数据库。可以在服务器组件里先预取当前用户的所有收藏文章ID,再传给客户端的Context,同时给服务器端的查询加缓存:
// 服务器端:获取用户收藏ID列表(带缓存) async function getFavoriteArticleIds(userId: string) { 'use server'; // 给查询加缓存,比如60秒刷新一次,或者用标签关联 const res = await db.query('SELECT article_id FROM favorites WHERE user_id = ?', [userId]); return res.map(item => item.article_id); } // 页面组件(服务器组件) export default async function ArticlePage({ params }: { params: { id: string } }) { const favoriteIds = await getFavoriteArticleIds(currentUserId); return ( <FavoritesProvider favoriteIds={favoriteIds}> <ArticleContent id={params.id} /> </FavoritesProvider> ); } // 客户端的Context组件 'use client'; import { createContext, useContext } from 'react'; const FavoritesContext = createContext<string[]>([]); export function FavoritesProvider({ children, favoriteIds }: { children: React.ReactNode; favoriteIds: string[] }) { return ( <FavoritesContext.Provider value={favoriteIds}> {children} </FavoritesContext.Provider> ); } // 收藏图标组件(客户端) export function FavoriteIcon({ articleId }: { articleId: string }) { const favoriteIds = useContext(FavoritesContext); const isFavorited = favoriteIds.includes(articleId); return ( <button disabled={isFavorited} onClick={() => handleToggleFavorite(articleId)}> {isFavorited ? '已收藏' : '收藏'} </button> ); }
2. 不用Context,直接在服务器组件传状态
如果页面结构简单,不需要跨多层组件传值,直接在服务器组件里查当前文章是否被收藏,把isFavorited属性传给客户端的图标组件就行,更轻量:
// 服务器端:查询单篇文章是否被收藏(带缓存) async function isArticleFavorited(articleId: string, userId: string) { 'use server'; const res = await db.query('SELECT 1 FROM favorites WHERE user_id = ? AND article_id = ?', [userId, articleId]); return res.length > 0; } // 页面组件(服务器组件) export default async function ArticlePage({ params }: { params: { id: string } }) { const isFavorited = await isArticleFavorited(params.id, currentUserId); return ( <div> <ArticleContent id={params.id} /> <FavoriteIcon isFavorited={isFavorited} articleId={params.id} /> </div> ); } // 客户端收藏图标 'use client'; export function FavoriteIcon({ isFavorited, articleId }: { isFavorited: boolean; articleId: string }) { return ( <button disabled={isFavorited} onClick={() => handleToggleFavorite(articleId)}> {isFavorited ? '已收藏' : '收藏'} </button> ); }
3. 优化缓存策略解决重复查询问题
你之前用Next缓存没效果,大概率是没给查询设置正确的缓存标识或者规则。针对用户专属的收藏数据,可以用缓存标签,在用户修改收藏时主动刷新缓存:
// 服务器端的收藏切换动作 'use server'; export async function toggleFavorite(articleId: string, userId: string) { // 先判断是否已收藏,执行添加/删除 const exists = await db.query('SELECT 1 FROM favorites WHERE user_id = ? AND article_id = ?', [userId, articleId]); if (exists.length > 0) { await db.query('DELETE FROM favorites WHERE user_id = ? AND article_id = ?', [userId, articleId]); } else { await db.query('INSERT INTO favorites (user_id, article_id) VALUES (?, ?)', [userId, articleId]); } // 刷新该用户的收藏缓存 revalidateTag(`user-favorites-${userId}`); } // 带标签的收藏列表查询 async function getFavoriteArticleIds(userId: string) { 'use server'; // 关联缓存标签,方便后续刷新 const res = await db.query('SELECT article_id FROM favorites WHERE user_id = ?', [userId], { tags: [`user-favorites-${userId}`] }); return res.map(item => item.article_id); }
总结
- React Context不是必须的,看你的组件层级复杂度决定用不用。
- 关键是利用Next.js 14的服务器组件能力,把查询放在服务器端,再配合缓存策略减少重复请求,同时用
revalidateTag保证用户修改收藏后状态能实时更新。
内容的提问来源于stack exchange,提问作者frogfood
相关产品推荐
相关产品推荐

