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
相关产品推荐
相关产品推荐

