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
相关产品推荐
相关产品推荐

