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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 13:22:27