NextJS 13客户端组件回调函数Props传递问题及替代方案咨询
解决Next.js 13中Client Component传递回调Props的序列化警告问题
在Next.js 13 App Router模式下,从Server Component传递给Client Component的props必须是可序列化的(如字符串、数字、对象、数组等JSON支持的类型),函数属于非序列化类型,因此会触发TS71007警告。以下是几种实现服务端分页/排序可复用Table组件的替代方案:
方案1:将状态提升到父级Client Component
如果父组件不需要保持Server Component特性,直接将其标记为"use client",把分页、排序状态和数据加载逻辑放在父组件中,Table组件仅负责渲染,通过props接收状态和更新函数。
父组件示例
'use client'; import { useState, useEffect } from 'react'; import DataGridTable from './DataGridTable'; export default function ParentPage() { const [sortModel, setSortModel] = useState<SortModel[]>([]); const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(10); const [data, setData] = useState<DataType[]>([]); const [totalCount, setTotalCount] = useState(0); // 服务端数据加载逻辑 useEffect(() => { const fetchData = async () => { const res = await fetch(`/api/data?sort=${JSON.stringify(sortModel)}&page=${page}&pageSize=${pageSize}`); const { rows, total } = await res.json(); setData(rows); setTotalCount(total); }; fetchData(); }, [sortModel, page, pageSize]); return ( <DataGridTable data={data} totalCount={totalCount} sortModel={sortModel} onSortModelChange={setSortModel} page={page} onPageChange={setPage} pageSize={pageSize} onPageSizeChange={setPageSize} /> ); }
Table组件示例
'use client'; import { DataGrid, GridColDef, SortModel } from '@mui/x-data-grid'; interface TableProps { data: DataType[]; totalCount: number; sortModel: SortModel[]; onSortModelChange: (model: SortModel[]) => void; page: number; onPageChange: (page: number) => void; pageSize: number; onPageSizeChange: (size: number) => void; } export default function DataGridTable({ data, totalCount, sortModel, onSortModelChange, page, onPageChange, pageSize, onPageSizeChange, }: TableProps) { const columns: GridColDef[] = [ // 定义列配置 ]; return ( <DataGrid rows={data} columns={columns} rowCount={totalCount} pagination page={page} pageSize={pageSize} onPageChange={onPageChange} onPageSizeChange={onPageSizeChange} sortingMode="server" sortModel={sortModel} onSortModelChange={onSortModelChange} paginationMode="server" /> ); }
方案2:用React Context封装状态与逻辑
当多个组件需要共享分页/排序状态时,可创建Context封装状态管理和数据加载逻辑,Table组件通过useContext获取所需方法,避免props传递回调。
创建Table Context
'use client'; import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; import { SortModel } from '@mui/x-data-grid'; interface TableContextType { data: DataType[]; totalCount: number; sortModel: SortModel[]; setSortModel: (model: SortModel[]) => void; page: number; setPage: (page: number) => void; pageSize: number; setPageSize: (size: number) => void; } const TableContext = createContext<TableContextType | undefined>(undefined); export function TableProvider({ children }: { children: ReactNode }) { const [sortModel, setSortModel] = useState<SortModel[]>([]); const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(10); const [data, setData] = useState<DataType[]>([]); const [totalCount, setTotalCount] = useState(0); useEffect(() => { const fetchData = async () => { const res = await fetch(`/api/data?sort=${JSON.stringify(sortModel)}&page=${page}&pageSize=${pageSize}`); const { rows, total } = await res.json(); setData(rows); setTotalCount(total); }; fetchData(); }, [sortModel, page, pageSize]); return ( <TableContext.Provider value={{ data, totalCount, sortModel, setSortModel, page, setPage, pageSize, setPageSize, }} > {children} </TableContext.Provider> ); } export function useTableContext() { const context = useContext(TableContext); if (!context) { throw new Error('useTableContext must be used within a TableProvider'); } return context; }
Table组件使用Context
'use client'; import { DataGrid, GridColDef } from '@mui/x-data-grid'; import { useTableContext } from './TableContext'; export default function DataGridTable() { const { data, totalCount, sortModel, setSortModel, page, setPage, pageSize, setPageSize, } = useTableContext(); const columns: GridColDef[] = [ // 列配置 ]; return ( <DataGrid rows={data} columns={columns} rowCount={totalCount} pagination page={page} pageSize={pageSize} onPageChange={(params) => setPage(params.page)} onPageSizeChange={(params) => setPageSize(params.pageSize)} sortingMode="server" sortModel={sortModel} onSortModelChange={setSortModel} paginationMode="server" /> ); }
在页面中使用Provider
// 可以是Server Component,因为Provider内部是Client逻辑 import { TableProvider } from './TableContext'; import DataGridTable from './DataGridTable'; export default function Page() { return ( <TableProvider> <DataGridTable /> </TableProvider> ); }
方案3:使用Server Actions处理数据请求
Next.js 13+支持Server Actions,可直接在Client Component中调用服务端函数处理数据加载,无需通过父组件回调传递逻辑。
定义Server Action
// app/actions.ts(无需标记"use client") 'use server'; import { SortModel } from '@mui/x-data-grid'; export async function fetchTableData(sortModel: SortModel[], page: number, pageSize: number) { // 这里直接写服务端逻辑,比如查询数据库 const rows = await db.data.findMany({ skip: page * pageSize, take: pageSize, orderBy: sortModel.map((item) => ({ [item.field]: item.sort })), }); const total = await db.data.count(); return { rows, total }; }
Client Component中调用Server Action
'use client'; import { useState, useEffect } from 'react'; import { DataGrid, GridColDef, SortModel } from '@mui/x-data-grid'; import { fetchTableData } from './actions'; interface TableProps { // 可传递列配置等序列化props columns: GridColDef[]; } export default function DataGridTable({ columns }: TableProps) { const [sortModel, setSortModel] = useState<SortModel[]>([]); const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(10); const [data, setData] = useState<DataType[]>([]); const [totalCount, setTotalCount] = useState(0); useEffect(() => { const loadData = async () => { const { rows, total } = await fetchTableData(sortModel, page, pageSize); setData(rows); setTotalCount(total); }; loadData(); }, [sortModel, page, pageSize]); return ( <DataGrid rows={data} columns={columns} rowCount={totalCount} pagination page={page} pageSize={pageSize} onPageChange={(params) => setPage(params.page)} onPageSizeChange={(params) => setPageSize(params.pageSize)} sortingMode="server" sortModel={sortModel} onSortModelChange={setSortModel} paginationMode="server" /> ); }
方案4:将Table组件的回调逻辑内联(适合简单场景)
如果数据加载逻辑比较简单,可直接在Table组件内部处理排序/分页变化,无需通过props传递回调。比如直接在onSortModelChange中调用数据请求。
'use client'; import { useState, useEffect } from 'react'; import { DataGrid, GridColDef, SortModel } from '@mui/x-data-grid'; interface TableProps { columns: GridColDef[]; apiUrl: string; // 传递可序列化的API地址 } export default function DataGridTable({ columns, apiUrl }: TableProps) { const [sortModel, setSortModel] = useState<SortModel[]>([]); const [page, setPage] = useState(0); const [pageSize, setPageSize] = useState(10); const [data, setData] = useState<DataType[]>([]); const [totalCount, setTotalCount] = useState(0); useEffect(() => { const fetchData = async () => { const res = await fetch(`${apiUrl}?sort=${JSON.stringify(sortModel)}&page=${page}&pageSize=${pageSize}`); const { rows, total } = await res.json(); setData(rows); setTotalCount(total); }; fetchData(); }, [sortModel, page, pageSize, apiUrl]); return ( <DataGrid rows={data} columns={columns} rowCount={totalCount} pagination page={page} pageSize={pageSize} onPageChange={(params) => setPage(params.page)} onPageSizeChange={(params) => setPageSize(params.pageSize)} sortingMode="server" sortModel={sortModel} onSortModelChange={setSortModel} paginationMode="server" /> ); }
内容的提问来源于stack exchange,提问作者Illia Chtchoma
相关产品推荐
相关产品推荐

