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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 13:06:26