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

MUI自定义表格结合Formik可编辑字段时排序性能缓慢问题

MUI自定义表格结合Formik可编辑字段时排序性能缓慢问题

我最近开发发票管理表格组件时碰到了性能瓶颈:我写了一个InvoiceTable组件,搭配自定义可编辑发票字段,用Formik统一管理表单状态。在Formik初始化前,我会先计算好initialSortedData;Formik启动后,所有表格数据、排序字段(sortBy)和排序顺序(sortOrder)都存在Formik的values状态里。

目前排序和编辑功能都能正常运行,但排序操作的响应速度特别慢——我的表格行数大概在50-100条,列数则在2到40列之间,这个规模按理说不该有这么明显的卡顿。

下面是我的组件代码,麻烦帮忙看看哪里可能出了问题:

function InvoiceTable() {
  const classes = useStyles();
  const globalClasses = globalStyles();
  const [saving, setSaving] = useState(false);
  const [generatingInvoice, setGeneratingInvoice] = useState(false);
  const [isAllSelected, setIsAllSelected] = useState(false);
  const { enqueueSnackbar } = useSnackbar();

  const {
    // State
    columnsOrder,
    apiLoading,
    clientDetails,
    query,
    invoiceTableData,
    noDataFound,
    page,
    rowsPerPage,
    totalRecords,
    // Functions
    dispatch,
    fetchInvoices,
    LoadingComponent,
    bulkEditApplied,
    runSafe,
    showDialog,
    confirm,
    cancel,
  } = useDashboard();

  // State to track the current sorting order and column
  const [order, setOrder] = useState("asc");
  const [orderBy, setOrderBy] = useState(columnsOrder[0]);
  const previousDataRef = useRef([]);

  useNavigationBlocker(bulkEditApplied, (proceedFn) => {
    runSafe(proceedFn);
  });

  /**
   * Saves invoice data to the database.
   * @param {{object}} data - The data to be saved to the database.
   * @returns None
   * @throws {error} If an error occurs during the saving process.
   */
  const saveInvoiceData = async (data, button = false) => {
    try {
      setSaving(true);
      const query = formatInvoiceDataAndGenerateQuery(data);
      await Endpoint.Dashboard.updateInvoiceTable({ query });
      if (button) {
        enqueueSnackbar("Data Updated", { variant: "success" });
      }
      if (bulkEditApplied) {
        dispatch({ type: actionTypes.SET_BULK_EDIT_APPLIED, payload: false });
      }
      setSaving(false);
    } catch (error) {
      setSaving(false);
      console.error("Error:", error);
    }
  };

  /**
   * Downloads an Excel file based on the provided data and client details.
   * @param {{object}} data - The data to be included in the Excel file.
   * @returns None
   */
  const downloadExcel = async (data) => {
    try {
      setGeneratingInvoice(true);
      await saveInvoiceData(data);
      // Filter: Keep columnsOrder that are either in `headersToKeep` OR not in `headersToRemove`
      const filteredHeaders = columnsOrder.filter(
        (header) => invoiceHeadersToKeep.includes(header) || !invoiceHeadersToRemove.includes(header)
      );
      const payload = {
        query: query,
        clientDetails: [clientDetails],
        invoiceHeaders: filteredHeaders,
      };
      const response = await Endpoint.Dashboard.generateInvoice(payload);
      if (response) {
        setGeneratingInvoice(false);
        window.open(response.data, "_blank");
        enqueueSnackbar("Invoice Downloaded!", { variant: "success" });
        await fetchInvoices();
      } else {
        setGeneratingInvoice(false);
        enqueueSnackbar("Something went wrong", { variant: "error" });
      }
    } catch (error) {
      setGeneratingInvoice(false);
      console.log(error);
    }
  };

  /**
   * Handles the event when a CSV file is chosen by the user.
   * Reads the file, processes the data, and updates the invoice table data accordingly.
   * @param {{Event}} event - The event object triggered by choosing a file.
   * @returns None
   */
  const handleChooseCSV = (event, values, setFieldValue) => {
    try {
      const file = event.target.files[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = async (e) => {
        const data = new Uint8Array(e.target.result);
        const workbook = XLSX.read(data, { type: "array" });
        const sheetName = workbook.SheetNames[0];
        const sheet = workbook.Sheets[sheetName];
        let jsonData = XLSX.utils.sheet_to_json(sheet, { raw: false });

        jsonData = jsonData.map((row) => {
          // Helper function for safe date conversion
          const parseDate = (dateValue) => {
            if (!dateValue) return null;
            const date = moment(dateValue, ["YYYY-MM-DD", "M/D/YYYY", moment.ISO_8601]);
            return date.isValid() ? date.format("YYYY-MM-DD") : null;
          };

          // Helper function for safe number parsing
          const parseNumber = (numValue) => {
            if (numValue === null || numValue === undefined || numValue === "") return undefined; // Changed to undefined
            const num = Number(numValue);
            return isNaN(num) ? undefined : num; // Changed to undefined
          };

          return {
            work_number: row.work_number || "", // Required field
            invoice_amount: parseNumber(row.invoice_amount),
            invoice_notes: row.invoice_notes || null,
            invoice_number: row.invoice_number || "",
            invoice_date: parseDate(row.invoice_date),
            amount_paid: parseNumber(row.amount_paid),
            check_number: row.check_number || null,
            check_date: parseDate(row.check_date),
            check_amount: parseNumber(row.check_amount),
            check_received_date: parseDate(row.check_received_date) || parseDate(row.payment_date), // Fallback to payment_date
          };
        });

        let tempData = [...values.data];
        jsonData.forEach((csvRow) => {
          const index = tempData.findIndex((item) => item.work_number === csvRow.work_number);
          if (index !== -1) {
            tempData[index] = { ...tempData[index], ...csvRow };
          }
        });
        setFieldValue("data", tempData);
      };
      reader.readAsArrayBuffer(file);
    } catch (error) {
      console.error("Error processing CSV:", error);
    }
  };

  // 注:排序相关的核心逻辑是基于Formik values中的数据进行排序,此处未完全贴出
}

我目前猜测是不是排序时直接操作Formik的values.data导致了不必要的重渲染?或者还有其他优化方向可以参考吗?

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 07:42:59