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

NextJS 13.4使用Context API遇fs模块未找到及状态共享问题

解决方案:在Next.js中结合服务器组件与Context API实现帖子过滤

核心矛盾是服务器组件无法使用客户端Hooks(如useContext),而你的帖子数据依赖fs模块只能在服务器端获取。解决思路是将数据获取逻辑和过滤渲染逻辑拆分到不同组件中:


1. 拆分组件职责

  • 服务器组件:仅负责获取所有帖子数据(因为需要调用依赖fs的getSortedPostsData)。
  • 客户端组件:接收服务器组件传递的帖子数据,使用useContext获取过滤状态,完成筛选和渲染。

2. 具体实现步骤

步骤1:创建客户端过滤组件

新建components/FilteredPosts.tsx,这是处理过滤逻辑的客户端组件:

"use client";

import { useFilterContext } from "@/store/FilterContext";
import getFormattedDate from "@/lib/getFormattedDate";
import Link from "next/link";

// 定义帖子类型(和你的BlogPost类型保持一致)
interface BlogPost {
  id: string;
  title: string;
  date: string;
}

export default function FilteredPosts({ posts }: { posts: BlogPost[] }) {
  const { filterQuery } = useFilterContext();

  // 根据搜索关键词过滤帖子
  const filteredPosts = posts.filter(post => 
    post.title.toLowerCase().includes(filterQuery.toLowerCase())
  );

  return (
    <div className="px-6 mx-auto max-w-4xl text-white">
      {filteredPosts.length === 0 ? (
        <p className="text-gray-400 mt-8">未找到匹配的帖子</p>
      ) : (
        filteredPosts.map(post => (
          <div key={post.id} className="mb-8 pb-4 border-b border-gray-700">
            <h2 className="text-2xl font-semibold">
              <Link 
                href={`/posts/${post.id}`} 
                className="hover:text-blue-300 transition-colors"
              >
                {post.title}
              </Link>
            </h2>
            <p className="text-gray-400 mt-1">{getFormattedDate(post.date)}</p>
          </div>
        ))
      )}
    </div>
  );
}

步骤2:修改帖子列表页面(服务器组件)

假设你的帖子列表在app/page.tsx,这里只负责获取数据并传递给客户端组件:

import { getSortedPostsData } from "@/lib/posts";
import FilteredPosts from "@/components/FilteredPosts";

export default async function Home() {
  // 服务器端获取所有帖子数据(仅在这里调用依赖fs的函数)
  const allPosts = getSortedPostsData();

  return (
    <main className="pt-8">
      {/* 将数据传递给客户端组件处理过滤 */}
      <FilteredPosts posts={allPosts} />
    </main>
  );
}

步骤3:确保搜索栏是客户端组件

你的Navbar中的搜索框需要修改为客户端组件(如果还没加'use client'),用来更新Context中的过滤状态:

"use client";

import { useFilterContext } from "@/store/FilterContext";

export default function SearchBar() {
  const { setFilterQuery } = useFilterContext();

  return (
    <input
      type="text"
      placeholder="搜索帖子..."
      onChange={(e) => setFilterQuery(e.target.value)}
      className="px-4 py-2 rounded-lg bg-slate-700 text-white border border-gray-600 focus:outline-none focus:ring-2 focus:ring-blue-500"
    />
  );
}

步骤4:确认FilterContext的客户端实现

确保你的FilterContext是客户端组件(已加'use client'):

"use client";

import { createContext, useContext, useState, ReactNode } from "react";

interface FilterContextType {
  filterQuery: string;
  setFilterQuery: (query: string) => void;
}

const FilterContext = createContext<FilterContextType | undefined>(undefined);

export function FilterContextProvider({ children }: { children: ReactNode }) {
  const [filterQuery, setFilterQuery] = useState("");

  return (
    <FilterContext.Provider value={{ filterQuery, setFilterQuery }}>
      {children}
    </FilterContext.Provider>
  );
}

export function useFilterContext() {
  const context = useContext(FilterContext);
  if (!context) {
    throw new Error("useFilterContext必须在FilterContextProvider内部使用");
  }
  return context;
}

3. 关键原理说明

  • 服务器组件仅做数据获取:因为fs模块只能在服务器端运行,所以数据获取逻辑必须放在无'use client'标识的组件中。
  • 客户端组件处理状态与渲染:所有需要使用useContext、useState等客户端Hooks的逻辑,都要放在加了'use client'的组件中,通过props接收服务器组件传递的数据。
  • Context作用域:根布局中的Providers已经包裹了整个应用,客户端组件可以正常访问Context中的过滤状态。

内容的提问来源于stack exchange,提问作者Joao Ghabriel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:32:10