reactjstypescriptmaterial-uireact-contextmui-x-data-grid

React MUI Data Grid: How to get index of the current row for React-Context


Context

In my application, there is an MUI Data Grid. In each row, there are 3 custom components: RowSlider, RowDate, and RowLock implementing the MUI Components Slider , Date Picker and Button respectively.

Visualization of the Data Grid

The components Slider and Date Picker both have an attribute called disabled like so:

(The *s are for easier visualization)

<Slider defaultValue={3} step={1} marks min={1} max={5} ***disabled={rowStates[rowIndex]}***/>

Functionality Goals

The functionality I want to achieve is that when the Lock Button is pressed, it will set the disabled attribute to true for both components and when pressed again will set it to false and so on and so forth.

I want this functionality scoped to each row, i.e., I do not want the "Lock button" in one row to affect the disabled attribute for components in another row

Problem

Right now, my problem is that I cannot find a way to obtain a row index from the MUI Data Grid.

This is what my column declarations for the Data Grid looks like:

import * as React from 'react';
import { GridColDef, GridCellParams } from '@mui/x-data-grid';
import RowDate from '../ui/table/row-date/RowDate';
import RowLock from '../ui/table/row-lock/RowLock';
import RowSlider from '../ui/table/row-slider/RowSlider';


export const columns: GridColDef[] = [
    {field: 'id', headerName: 'ID', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell'},
    {field: 'word', headerName: 'Word', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell' },
    {field: 'romanization', headerName: 'Romanization', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell'},
    {field: 'definition', headerName: 'Definition', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell'},
    {field: 'comfortability', headerName: 'Comfortability', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell',
        renderCell: (params) => {
            return(
                
                <RowSlider rowIndex={params.rowNode.depth}></RowSlider>
            );
        }
    },
    {field: 'lastModified', headerName: 'Last Checked', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell',
        renderCell: (params) => {
            return(
                    <RowDate rowIndex={params.rowNode.depth}></RowDate>
            );
        }
    },
    {field: 'lock', headerName: 'Lock', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell',
        renderCell: (params) => {
            return(
                    <RowLock rowIndex={params.rowNode.depth}></RowLock>            
                );
        }
    },
]

export interface TableRow {
    id: number,
    word: string;
    romanization: string,
    definition: string;
    comfortability: number;
    lastModified: Date | null;
    lock: boolean;
};

As you can see, I need to be able to pass in the Data Grid "Row Index" (if it exists) into these components as a prop but I have not found a way to do so.

I am planning on tying this index to a React Context declared inside Table.tsx:

import * as React from 'react';
import { createContext, useContext } from 'react';
import { Box, alpha, styled } from '@mui/material';
import { DataGrid, GridToolbar, GridRowsProp, GridColDef, gridClasses, GridFilterModel, GridColumnVisibilityModel} from '@mui/x-data-grid';
import { columns} from '../../data/DataInterface';
import { data } from '../../data/Data';
import { DataTable } from './styled-data-grid/StyledDataGrid';

const RowStateContext = createContext<{ rowStates: boolean[]; setRowState: (index: number, value: boolean) => void } | undefined>(undefined);

export const useRowStateContext = () => {
  const context = useContext(RowStateContext);
  return context;
};

export default function Table() {

  const [rowStates, setRowStates] = React.useState<boolean[]>(Array(data.length).fill(false)); // Initialize rowStates array

  const setRowState = (index: number, value: boolean) => {
    const newStates = [...rowStates];
    newStates[index] = value;
    setRowStates(newStates);
  };


  const [filterModel, setFilterModel] = React.useState<GridFilterModel>({
      items: [],
      quickFilterExcludeHiddenColumns: false,
      quickFilterValues: [],
    });

  const [columnVisibilityModel, setColumnVisibilityModel] = React.useState<GridColumnVisibilityModel>({});

  return(
      <RowStateContext.Provider value={{ rowStates, setRowState }}> // Provide row state context

        <DataTable
          checkboxSelection
          columns={columns}
          columnVisibilityModel={columnVisibilityModel}
          disableColumnFilter
          disableDensitySelector
          disableRowSelectionOnClick
          filterModel={filterModel}
          getRowClassName={(params) =>
            params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
          }
          onColumnVisibilityModelChange={(newModel) =>
              setColumnVisibilityModel(newModel)
          }
          onFilterModelChange={(newModel) => setFilterModel(newModel)}
          rows={data}
          slots={{ toolbar: GridToolbar }}
          slotProps={{ toolbar: { showQuickFilter: true } }}
        />

      </RowStateContext.Provider>
  );
}

Current Results

As of right now, the functionality I am able to produce is that, pressing one of the Lock buttons results in all components in every row being disabled, i.e., its not scoped.

I believe the solution lies within the column declarations within the renderCell property:

{field: 'lock', headerName: 'Lock', flex: 1, headerClassName: 'header-cell', cellClassName: 'body-cell',
        renderCell: (params) => {
            return(
                    <RowLock rowIndex={params.rowNode.depth}></RowLock>            
                );
        }
    },

However, I don't know how to do this. As you can see with rowNode.depth being my latest (lazy) attempt. If I need to switch over to a Data Table instead of a Data Grid please let me know to (if it would be easier)


Solution

  • I implemented a solution using hooks. Let me know if this works. MuiDataGridLock