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

Material UI DataGrid双renderCell配置避免重复渲染问题

问题描述

使用Material UI的DataGrid渲染表格时,为“Level by user”和“Level by referent”两列配置自定义renderCell后,出现列互相替换的异常:默认情况下“Level by referent”列会替换“Level by user”列,调换两列顺序后则变为“Level by user”列替换“Level by referent”列,无法同时显示两个自定义列。

已尝试方案

  • 调换两列在columns数组中的位置
  • 将“Level by user”放在数组开头,把“Level by referent”加入concat部分
    以上方案均未解决问题。
问题原因

Material UI的DataGrid要求每一列必须设置唯一的field属性作为列的标识。你定义的两个自定义列未配置field,导致DataGrid无法区分它们,后定义的列会覆盖先定义的列。

解决方案

为两个自定义列分别添加唯一的field值,同时建议将renderCell的参数名从convertedRows改为params,避免与外部变量重名,提升代码可读性。修改后的columns定义如下:

let columns =
  convertedRows?.length > 0
    ? [
        {
          field: 'levelByUser', // 添加唯一标识字段
          headerName: 'Level by user',
          headerAlign: 'center',
          align: 'center',
          renderCell: (params) => {
            const userLevel = params?.row?.Level?.find(
              (level) => level?.Role === 'User'
            );
            return (
              <Typography
                sx={{
                  textAlign: 'center',
                  fontSize: '0.875rem',
                  width: 150,
                  height: 40,
                  ...(userLevel?.Level === 'Expert' && {
                    color: 'green',
                    fontWeight: 'bold',
                  }),
                  ...(userLevel?.Level === 'Confirmed' && {
                    color: 'green',
                  }),
                  ...(userLevel?.Level === 'Intermediate' && {
                    color: 'orange',
                  }),
                  ...(userLevel?.Level === 'Beginner' && {
                    color: 'lightsalmon',
                  }),
                  ...(userLevel?.Level === 'Unknow' && { color: 'grey' }),
                }}
              >
                {userLevel?.Level}
              </Typography>
            );
          },
        },
        {
          field: 'levelByReferent', // 添加唯一标识字段
          headerName: 'Level by referent',
          headerAlign: 'center',
          align: 'center',
          renderCell: (params) => {
            const referentLevel = params?.row?.Level?.find(
              (level) => level?.Role === 'Referent'
            );
            console.log('2', referentLevel);

            return (
              <Typography
                sx={{
                  textAlign: 'center',
                  fontSize: '0.875rem',
                  width: 150,
                  height: 40,
                  ...(referentLevel?.Level === 'Expert' && {
                    color: 'green',
                    fontWeight: 'bold',
                  }),
                  ...(referentLevel?.Level === 'Confirmed' && {
                    color: 'green',
                  }),
                  ...(referentLevel?.Level === 'Intermediate' && {
                    color: 'orange',
                  }),
                  ...(referentLevel?.Level === 'Beginner' && {
                    color: 'lightsalmon',
                  }),
                  ...(referentLevel?.Level === 'Unknow' && { color: 'grey' }),
                }}
              >
                {referentLevel?.Level}
              </Typography>
            );
          },
        },
      ].concat(
        Object.keys(convertedRows[0])
          .filter(
            (field) =>
              field !== 'updated_at' &&
              field !== 'created_at' &&
              field !== 'id' &&
              field !== 'Level'
          )
          .map((field) => {
            return {
              field: field,
              headerName: field,
              flex: 1,
            };
          })
      )
    : [];
原相关代码

TcgTable组件代码

import React from 'react';
import { useSelector } from 'react-redux';
import { TableReusable } from '../../reusable/TableReusable';
import { Box, CircularProgress, Typography } from '@mui/material';
import useLevelToLetterConversion from '../../reusable/UseLevelConverter';

const TcgTable = () => {
  const oneUserSkill = useSelector((state) => state.user?.oneUserSkill);

  const rows = oneUserSkill?.skills?.tcg;
  const convertedRows = useLevelToLetterConversion(rows);

  let columns =
    convertedRows?.length > 0
      ? [    {
        headerName: 'Level by user',
        headerAlign: 'center',
        align: 'center',
        renderCell: (convertedRows) => {
          const userLevel = convertedRows?.row?.Level?.find(
            (level) => level?.Role === 'User'
          );
          return (
            <Typography
              sx={{
                textAlign: 'center',
                fontSize: '0.875rem',
                width: 150,
                height: 40,

                ...(userLevel?.Level === 'Expert' && {
                  color: 'green',
                  fontWeight: 'bold',
                }),
                ...(userLevel?.Level === 'Confirmed' && {
                  color: 'green',
                }),
                ...(userLevel?.Level === 'Intermediate' && {
                  color: 'orange',
                }),
                ...(userLevel?.Level === 'Beginner' && {
                  color: 'lightsalmon',
                }),
                ...(userLevel?.Level === 'Unknow' && { color: 'grey' }),
              }}
            >
              {userLevel?.Level}
            </Typography>
          );
        },
      },
          {
            headerName: 'Level by referent',
            headerAlign: 'center',
            align: 'center',
            renderCell: (convertedRows) => {
              const referentLevel = convertedRows?.row?.Level?.find(
                (level) => level?.Role === 'Referent'
              );
              console.log('2', referentLevel);

              return (
                <Typography
                  sx={{
                    textAlign: 'center',
                    fontSize: '0.875rem',
                    width: 150,
                    height: 40,

                    ...(referentLevel?.Level === 'Expert' && {
                      color: 'green',
                      fontWeight: 'bold',
                    }),
                    ...(referentLevel?.Level === 'Confirmed' && {
                      color: 'green',
                    }),
                    ...(referentLevel?.Level === 'Intermediate' && {
                      color: 'orange',
                    }),
                    ...(referentLevel?.Level === 'Beginner' && {
                      color: 'lightsalmon',
                    }),
                    ...(referentLevel?.Level === 'Unknow' && { color: 'grey' }),
                  }}
                >
                  {referentLevel?.Level}
                </Typography>
              );
            },
          },
        ].concat(
          Object.keys(convertedRows[0])
            .filter(
              (field) =>
                field !== 'updated_at' &&
                field !== 'created_at' &&
                field !== 'id' &&
                field !== 'Level'
            )
            .map((field) => {
              return {
                field: field,
                headerName: field,
                flex: 1,
              };
            })
        )
      : [];


  return <TableReusable data={convertedRows} columns={columns} bool={false} />;
};

TableReusable组件代码

import React from 'react';
import {
  DataGrid,
  GridToolbar,
  GridToolbarQuickFilter,
} from '@mui/x-data-grid';
import { useDispatch } from 'react-redux';
import { Box, Stack } from '@mui/material';

export const TableReusable = ({ data, columns, bool, setSkillSelected }) => {
  const dispatch = useDispatch();
  const onRowsSelectionHandler = (ids) => {
    const selectedRowsData = ids?.map((id) =>
      data?.find((row) => row?._id === id || row?.id === id)
    );
    if (setSkillSelected) {
      dispatch(setSkillSelected(selectedRowsData));
    }
  };

  function NoRowsOverlay() {
    return (
      <Stack height="100%" alignItems="center" justifyContent="center">
        No skills for the moment
      </Stack>
    );
  }

  function NoResultsOverlay() {
    return (
      <Stack height="100%" alignItems="center" justifyContent="center">
        No results
      </Stack>
    );
  }

  function QuickSearchToolbar() {
    return (
      <Box
        sx={{
          display: 'flex',
          justifyContent: 'space-between',
          p: 0.5,
          pb: 0,
        }}
      >
        <GridToolbar />
        <GridToolbarQuickFilter
          quickFilterParser={(searchInput) =>
            searchInput
              .split(',')
              .map((value) => value.trim())
              .filter((value) => value !== '')
          }
        />
      </Box>
    );
  }

  return (
    <>
      <DataGrid
        density="comfortable"
        sx={{
          height: data?.length > 0 ? '100%' : 250,
          width: '100%',
          background: 'white',
          '.MuiDataGrid-columnHeaderTitle': {
            fontWeight: 'bold !important',
            overflow: 'visible !important',
          },
        }}
        rows={data}
        getRowId={(row) => row?.id || row?._id || row?.CommonId}
        columns={columns}
        initialState={{
          pagination: { paginationModel: { pageSize: 10 } },
          ...data.initialState,
        }}
        onRowSelectionModelChange={(ids) => onRowsSelectionHandler(ids)}
        components={{ NoRowsOverlay, NoResultsOverlay }}
        pageSizeOptions={[5, 10, 15, 20, 50, 100]}
        checkboxSelection={bool}
        slots={{ toolbar: QuickSearchToolbar }}
        componentsProps={{
          pagination: {
            labelRowsPerPage: 'Nb by page',
          },
        }}
      />
    </>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 01:09:51