Next.js14中用Redux Toolkit查数据库遇child_process模块缺失问题
问题根源
MongoDB驱动依赖Node.js核心模块child_process,但标记为'use client'的组件运行在浏览器环境,根本没有这个模块——你直接在Redux的async thunk里调用数据库操作,等于把服务端代码硬塞到客户端跑,必然报错。
解决方案
1. 把数据库操作完全隔离到服务端
把data.ts改成Server Action,加上'use server'指令,确保所有数据库逻辑只在服务端执行:
// lib/api/data.ts 'use server'; import { MongoClient } from 'mongodb'; export async function fetchProducts(filterParams: Record<string, any>) { const client = await MongoClient.connect(process.env.MONGODB_URI!); const db = client.db(); // 这里写你的过滤查询逻辑 const products = await db.collection('products') .find(filterParams) .toArray(); await client.close(); return products; }
2. 修改Redux Thunk,让它调用Server Action
原来的thunk不要直接操作数据库,改成调用上面的Server Action(Next.js会自动处理客户端到服务端的请求):
// lib/redux/features/filterSlice.ts import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import { fetchProducts } from '@/lib/api/data'; export const getProducts = createAsyncThunk( 'filters/getProducts', async (filterParams: Record<string, any>) => { // 调用服务端的fetchProducts,而非直接操作数据库 const data = await fetchProducts(filterParams); return data; } ); const filterSlice = createSlice({ name: 'filters', initialState: { products: [], loading: false, error: null as string | null, }, reducers: {}, extraReducers: (builder) => { builder .addCase(getProducts.pending, (state) => { state.loading = true; state.error = null; }) .addCase(getProducts.fulfilled, (state, action) => { state.loading = false; state.products = action.payload; }) .addCase(getProducts.rejected, (state, action) => { state.loading = false; state.error = action.error.message || '获取产品失败'; }); }, }); export default filterSlice.reducer;
3. 处理Query参数传递
Next.js 14 App Router里不用getServerSideProps了,直接在Server Component里拿searchParams,再传给客户端组件:
// app/search/page.tsx(Server Component,不用加'use client') import ProductsContent from '@/components/products-content'; export default async function SearchPage({ searchParams }: { searchParams: Record<string, string | string[] | undefined> }) { // 提取并整理url里的查询参数 const filterParams = { keyword: searchParams.keyword?.toString() || '', category: searchParams.category?.toString() || '', }; return <ProductsContent initialFilters={filterParams} />; }
然后在客户端组件里用这些初始参数触发Thunk:
// components/products-content/index.tsx('use client'组件) 'use client'; import { useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { getProducts } from '@/lib/redux/features/filterSlice'; export default function ProductsContent({ initialFilters }: { initialFilters: Record<string, any> }) { const dispatch = useDispatch(); const { products, loading, error } = useSelector((state: any) => state.filters); useEffect(() => { // 页面加载时用初始参数请求数据 dispatch(getProducts(initialFilters)); }, [dispatch, initialFilters]); return ( <div> {loading && <p>加载中...</p>} {error && <p className="text-red-500">{error}</p>} {products.map((product: any) => ( <div key={product._id} className="border p-4 mb-2"> <h3>{product.name}</h3> <p>{product.price}</p> </div> ))} </div> ); }
为什么之前的方法无效?
getServerSideProps是Pages Router的API,App Router里已经废弃,改用Server Component直接获取参数。- 你之前尝试的拆分代码没到位——只要Redux Thunk里还直接引入MongoDB驱动,它就会跟着客户端组件打包到浏览器,必然触发模块找不到的错误。必须把数据库操作完全放到服务端,客户端只负责调用服务端接口。
内容的提问来源于stack exchange,提问作者equinox_xy
相关产品推荐
相关产品推荐

