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

MUI DataGrid通过QuickSearchToolbar调用外部API实现服务端筛选

解决MUI DataGrid基于外部API的服务端快速筛选问题

核心问题分析

  • 状态定义错误:searchInput状态声明在组件外部,既违反React状态管理规则,也无法在App组件与QuickSearchToolbar之间共享状态。
  • 组件通信缺失:自定义Toolbar组件未接收父组件的状态更新方法,无法将搜索输入值同步到App组件。
  • Effect依赖不全:数据请求的useEffect未监听searchInput变化,导致搜索输入改变时不会触发API请求刷新数据。
  • 分页逻辑冲突:初始page值设为0,结合DataGrid的page={pageState.page - 1}会导致分页参数异常(API请求page=0,而DataGrid显示page=-1)。
  • total字段误用:DataGrid的rowCount需要总数据条数,原代码使用totalPages(总页数)会导致分页控件显示错误。

修复后的完整代码

import { Box, Container } from "@mui/material";
import { DataGrid, GridToolbarQuickFilter } from "@mui/x-data-grid";
import { useEffect, useState } from "react";
import { BASE_URL } from "../services/urls";
import jwtInterceptor from "../components/shared/jwtInterceptor";

// 自定义Toolbar,通过props接收搜索状态与更新方法
const QuickSearchToolbar = ({ searchInput, setSearchInput }) => {
  return (
    <Box sx={{ p: 0.5, pb: 0 }}>
      <GridToolbarQuickFilter
        value={searchInput}
        onChange={(e) => setSearchInput(e.target.value)}
        placeholder="جستجو در نام شرکت..."
      />
    </Box>
  );
};

function App() {
  const [pageState, setPageState] = useState({
    isLoading: false,
    data: [],
    total: 0,
    page: 1, // 初始page设为1,匹配API分页逻辑(假设API从1开始计数)
    pageSize: 5,
  });
  // 将搜索状态移到App组件内部
  const [searchInput, setSearchInput] = useState("");

  const [columns] = useState([
    {
      field: "id",
      headerName: "شناسه",
      flex: 1,
      minWidth: 120,
    },
    {
      field: "httpMethod",
      headerName: "متد درخواست",
      flex: 1,
      minWidth: 150,
    },
    {
      field: "createdBy",
      headerName: "نام شرکت",
      flex: 1,
      minWidth: 150,
    },
    {
      field: "uri",
      headerName: "آدرس یو آر ال",
      flex: 1,
      minWidth: 500,
    },
    {
      field: "responseBody",
      headerName: "بدنه پاسخ",
      flex: 1,
      minWidth: 600,
    },
    {
      field: "requestBody",
      headerName: "بدنه درخواست",
      flex: 1,
      minWidth: 400,
    },
    {
      field: "requestHeader",
      headerName: "هدر درخواست",
      flex: 1,
      minWidth: 400,
    },
    {
      field: "responseHeader",
      headerName: "هدر پاسخ",
      flex: 1,
      minWidth: 400,
    },
    { field: "requestTime", headerName: "زمان درخواست" },
    {
      field: "responseTime",
      headerName: "زمان پاسخ",
    },
    {
      field: "responseStatus",
      headerName: "وضعیت پاسخ",
    },
    {
      field: "responseException",
      headerName: "خطا",
    },
    { field: "createdAt", headerName: "زمان ایجاد" },
    { field: "updatedAt", headerName: "زمان بروز رسانی" },
    { field: "updatedBy", headerName: "کاربر بروز رسانی" },
  ]);

  useEffect(() => {
    const fetchData = async () => {
      setPageState(old => ({ ...old, isLoading: true }));
      try {
        const response = await jwtInterceptor.post(
          `${BASE_URL}log/thirdPartyServices/filter?page=${pageState.page}&size=${pageState.pageSize}`,
          {
            httpMethod: "POST",
            createdBy: searchInput || null, // 空输入时传递null,符合后端参数预期
            uri: null,
            responseException: null,
            responseStatus: null,
          }
        );

        setPageState(old => ({
          ...old,
          isLoading: false,
          data: response.data.content,
          total: response.data.totalElements, // 使用总条数作为rowCount的数据源
        }));
      } catch (err) {
        console.error("دریافت داده‌ها با خطا مواجه شد:", err);
        setPageState(old => ({ ...old, isLoading: false }));
      }
    };

    fetchData();
  }, [pageState.page, pageState.pageSize, searchInput]); // 添加searchInput到依赖数组,触发搜索时的请求

  return (
    <Box>
      <Container style={{ marginTop: 100, marginBottom: 100 }}>
        <DataGrid
          autoHeight
          rows={pageState.data}
          rowCount={pageState.total}
          loading={pageState.isLoading}
          rowsPerPageOptions={[10, 30, 50, 70, 100]}
          pagination
          page={pageState.page - 1} // DataGrid的page从0开始,对应API的page从1开始
          pageSize={pageState.pageSize}
          paginationMode="server"
          onPageChange={(newPage) => {
            setPageState(old => ({ ...old, page: newPage + 1 }));
          }}
          onPageSizeChange={(newPageSize) =>
            setPageState(old => ({ ...old, pageSize: newPageSize, page: 1 })) // 切换每页条数时重置到第一页
          }
          columns={columns}
          components={{ 
            Toolbar: () => <QuickSearchToolbar searchInput={searchInput} setSearchInput={setSearchInput} /> 
          }}
        />
      </Container>
    </Box>
  );
}

export default App;

关键优化点说明

  1. 状态管理修正:将searchInput移到App组件内部,通过props传递给Toolbar,实现父子组件状态同步。
  2. 搜索控件绑定:使用value和onChange替代错误的quickFilterParser,正确捕获搜索输入值。
  3. 依赖数组完善:添加searchInput到useEffect依赖,确保搜索输入变化时自动触发API请求。
  4. 分页逻辑统一:初始page设为1,配合DataGrid的page={pageState.page - 1},保证前后端分页参数匹配。
  5. 错误处理:添加try/catch捕获API请求异常,避免页面一直处于加载状态。
  6. 分页重置:切换每页条数时自动重置到第一页,避免出现无数据的页码。

内容的提问来源于stack exchange,提问作者Kasra Roshan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 07:07:55