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

如何在MUI X Data Grid编辑单元格时更新useState中的总计值?

MUI X Data Grid编辑单元格时外部总计值无法更新的解决方法

问题背景

在使用MUI X Data Grid的编辑模式时,允许用户修改qteLivree(已交付数量),每行的prixTotal(小计)能正常计算,但外部展示的总计值无法同步更新。尝试过onCellEditStop无法获取最新值,使用processRowUpdate时更新一行后另一行会恢复初始值。

问题根源

当前代码中rows是直接从commande props生成的静态变量,并非React状态。Data Grid内部编辑行数据后,外部的rows不会同步更新,导致total状态始终基于初始值计算。另外使用processRowUpdate时未正确更新状态中的行数据,才会出现行数据回退的问题。

解决方案

方案一:将rows转为状态,通过processRowUpdate同步更新行数据与总计

把行数据转为React状态,通过processRowUpdate处理编辑后的行数据更新,同时自动重新计算总计值。

修改后的完整代码:

import {
    DataGrid,
    GridColDef,
    GridCellParams,
    gridClasses,
    useGridApiRef,
    GridRowModel
} from "@mui/x-data-grid";
import {Box} from "@mui/material";
import * as React from "react";
import {DetailCommande, Ligne} from "../../../../app/models/commande";
import {useState, useEffect} from "react";

type row = {
    id: number,
    reference: string | null,
    qte: number,
    qteLivree: number,
    designation: string,
    prixUnitaire: number,
}

const TableauValidationBL = ({commande}: { commande: DetailCommande }) => {
    const apiRef = useGridApiRef()

    const columns: GridColDef[] = [
        {field: 'reference', headerName: 'Référence', width: 120, headerClassName: 'header'},
        {field: 'designation', headerName: 'Désignation', flex: 1, headerClassName: 'header'},
        {
            field: 'qteLivree',
            headerName: 'Qté validé',
            width: 120,
            headerClassName: 'header',
            editable: true,
            type: 'number'
        },
        {field: 'prixUnitaire', headerName: 'Prix Unitaire', width: 120, headerClassName: 'header'},
        {
            field: 'prixTotal', headerName: 'Prix Total', width: 120, headerClassName: 'header',
            valueGetter: (_, row) => {
                return (row.prixUnitaire * row.qteLivree).toFixed(3);
            }
        },
    ];

    // 将rows转为React状态,初始值从commande生成
    const [rows, setRows] = useState<row[]>(commande ? commande.lignes.map((ligne: Ligne) => {
                return {
                    id: ligne.idPackaging,
                    reference: ligne.refFournisseur,
                    qte: ligne.qteCmd,
                    qteLivree: ligne.qteCmd,
                    designation: ligne.nomArticle,
                    prixUnitaire: ligne.prixUnitaire,
                }
            }) : []
    );

    const [total, setTotal] = useState(0);

    // 监听rows变化,自动重新计算总计
    useEffect(() => {
        const calculatedTotal = rows.reduce((acc, row) => acc + row.prixUnitaire * row.qteLivree, 0);
        setTotal(calculatedTotal);
    }, [rows]);

    // 处理行更新,替换对应id的行数据
    const processRowUpdate = (newRow: GridRowModel) => {
        const updatedRows = rows.map(row => row.id === newRow.id ? {...row, ...newRow} : row);
        setRows(updatedRows);
        return newRow;
    };

    return (
        <Box
            sx={{
                '.header': {backgroundColor: 'lightgrey', fontSize: '1.2em'},
                [`.${gridClasses.cell}.changed`]: {
                    backgroundColor: '#76CDCD',
                },
                [`.${gridClasses.cell}.unchanged`]: {
                    backgroundColor: '#7AA95C',
                },
            }}
        >
            <p>{total.toFixed(3)}</p>
            <DataGrid
                autoHeight
                hideFooter
                getRowHeight={() => 'auto'}
                rows={rows}
                columns={columns}
                apiRef={apiRef}
                showCellVerticalBorder
                getCellClassName={(params: GridCellParams) => {
                    if (params.field === 'qteLivree' && params.value !== params.row.qte) {
                        return 'changed';
                    }
                    if (params.field === 'qteLivree' && params.value === params.row.qte) {
                        return 'unchanged';
                    }
                    return '';
                }}
                processRowUpdate={processRowUpdate}
                editMode="row"
            />
        </Box>
    );
};

export default TableauValidationBL;

关键修改点

  • 将rows转为useState状态,确保行数据变化时触发组件重渲染
  • 使用useEffect监听rows变化,自动重新计算并更新总计值
  • 实现processRowUpdate方法,正确更新状态中的行数据,避免编辑后行数据回退
  • 开启editMode="row"(若需单元格编辑可改为cell模式,processRowUpdate依然生效)

方案二:通过apiRef获取最新行数据计算总计

如果不想维护rows状态,可以直接通过apiRef从Data Grid实例中获取最新行数据,在单元格编辑完成后重新计算总计:

// 省略其他代码...
const [total, setTotal] = useState(rows.reduce((acc, row) => acc + row.prixUnitaire * row.qteLivree, 0));

const handleCellEditCommit = () => {
    // 通过apiRef获取所有行的最新数据
    const allRows = apiRef.current.getRows();
    const calculatedTotal = allRows.reduce((acc, row) => acc + row.prixUnitaire * row.qteLivree, 0);
    setTotal(calculatedTotal);
};

return (
    <Box>
        <p>{total.toFixed(3)}</p>
        <DataGrid
            // 保留原有属性...
            onCellEditCommit={handleCellEditCommit}
        />
    </Box>
);

这种方式无需维护行数据状态,直接从Data Grid内部获取最新数据,适合简单场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 08:06:09