Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to keep my row selected in MUI X Data Grid when the search or filter value change

I am using mMUI X Data Grid to show a list of article. The user can select one or multiple row/article.

I have implemented my own filter and my own search bar.

When the user select row and then filter or search he will lost the previous row selected.

How to avoid this and keep the row selected ?

I think the problem is that when a user select an article in initial rows and then filter the rows, It will reduce the rows and the selected article who are not in the filter rows will just unselect

This is my parent component where i have the initial row/article list, and the search, filter and selection component

import * as React from 'react';
import { Paper } from '@mui/material';

import { FilterArticle } from './FilterArticle';
import { SearchArticle } from './SearchArticle';
import { useDispatch } from 'react-redux';
import { setActualTitle } from 'feature/title.slice';
import { SelectionArticle } from './SelectionArticle';

export const SelectArticle = ({ control, watch, register, setValue }) => {
 const list = [
    {
      id: 1,
      name: 'Snow',
      age: 35,
      stack: '3',
      service: '1',
      asset: '1',
      price: [{ SSL: 5 }, { SSB: 10 }, { SSF: 15 }],
      quantity: 5,
    },
    {
      id: 2,
      name: 'Lannister',
      age: 42,
      stack: '3',
      service: '1',
      asset: '2',
      price: [{ SSL: 5 }, { SSB: 10 }, { SSF: 15 }],
      quantity: 5,
    },
    {
      id: 3,
      name: 'Lannister',
      age: 45,
      stack: '3',
      service: '1',
      asset: '3',
      price: [{ SSL: 5 }, { SSB: 10 }, { SSF: 15 }],
      quantity: 5,
    },
  ];

  const dispatch = useDispatch();
  
  
  const [rows, setRows] = React.useState(list);

  React.useEffect(() => {
    dispatch(setActualTitle('Articles professional services'));
   }, []);

  return (
    <div className="form">
      <label className="subtitle">Select Articles</label>{' '}
        <Paper className="form_group" style={{ height: 550, width: '60vw' }}>
          <FilterArticle
            setRows={setRows}
            watch={watch}
            list={list}
            register={register}
          />
          <SearchArticle setRows={setRows} list={list} />

          <SelectionArticle
            list={list}
            setValue={setValue}
            control={control}
            rows={rows}
            watch={watch}
          />
        </Paper>
      
    </div>
  );
};

Here is my searchArticle component, where i put the logic of search article

import { Box, TextField } from '@mui/material';
import { RxCross2 } from 'react-icons/rx';
import React from 'react';

export const SearchArticle = ({ setRows, list }) => {
  const [search, setSearch] = React.useState('');

  const requestSearch = (search) => {
    if (search === '') {
      setRows(list);
      return;
    }
    const filteredRows = list?.filter((row) => {
      return (
        row.name?.toLowerCase().includes(search?.toLowerCase()) ||
        row.service?.toLowerCase().includes(search?.toLowerCase()) ||
        row.asset?.toLowerCase().includes(search?.toLowerCase()) ||
        (row.stack && row.stack.toString().includes(search))
      );
    });
    setRows(filteredRows);
  };

  React.useEffect(() => {
    requestSearch(search);
  }, [search]);

  const cancelSearch = () => {
    setSearch('');
  };

  return (
    <Box sx={{ m: 1, minWidth: 120, display: 'flex', alignItems: 'center' }}>
      <TextField
        id="standard-basic"
        label="search..."
        variant="standard"
        className="searchBar_input"
        placeholder="search..."
        value={search}
        onChange={(e) => setSearch(e.target.value)}
      />
      <RxCross2 onClick={cancelSearch} />
    </Box>
  );
};

Here the SelectionArticle component with the DataGrid

import { DataGrid } from '@mui/x-data-grid';
import React, { useEffect, useState } from 'react';
import { Stack } from 'react-bootstrap';
import { useFieldArray } from 'react-hook-form';

export const SelectionArticle = ({ control, setValue, rows, watch }) => {
  const entityConcerned = watch('OffersInfos.entityConcerned');
  const articleSelected = watch('Articles');
  const [selectedRows, setSelectedRows] = useState(articleSelected);

  const [selectionModel, setSelectionModel] = useState(() =>
    rows.filter((r) => r.id > 2).map((r) => r.id)
  );

  useEffect(() => {
    replace(selectedRows);
  }, [selectedRows]);

  const columns = [
    { field: 'id', headerName: 'ID', width: 70 },
    { field: 'name', headerName: 'name', width: 130 },
    {
      field: 'age',
      headerName: 'Age',
      type: 'number',
      width: 90,
    },
    {
      field: 'stack',
      headerName: 'Stacks',
      width: 90,
    },
    {
      field: 'service',
      headerName: 'service',
      width: 90,
    },
    {
      field: 'asset',
      headerName: 'asset',
      width: 90,
    },
    {
      field: 'quantity',
      headerName: 'quantity',
      width: 90,
    },
    {
      field: 'price',
      headerName: 'price',
      width: 90,
      renderCell: (params) => {
        const priceObj = params.row.price.find((obj) => obj[entityConcerned]);
        const price = priceObj ? priceObj[entityConcerned] : '';

        return <span>{price}</span>;
      },
    },
  ];

  const onRowsSelectionHandler = (ids) => {
    const selectedRowsData = ids.map((id) => rows.find((row) => row.id === id));
    setSelectedRows(selectedRowsData);
  };

  const { replace } = useFieldArray({
    control,
    name: 'Articles',
  });

  function NoRowsOverlay() {
    return (
      <Stack height="100%" alignItems="center" justifyContent="center">
        Aucun article
      </Stack>
    );
  }
  function NoResultsOverlay() {
    return (
      <Stack height="100%" alignItems="center" justifyContent="center">
        Aucun résultat
      </Stack>
    );
  }

  useEffect(() => {
    // Update the selection model based on the selected articles
    const selectedArticleIds = articleSelected?.map((article) => article?.id);
    const selectedRowsIds = rows
      .filter((row) => selectedArticleIds?.includes(row?.id))
      .map((row) => row?.id);
    setSelectionModel(selectedRowsIds);
  }, [articleSelected]);

  return (
    <DataGrid
      rows={rows}
      getRowId={(row) => row?.id}
      columns={columns}
      checkboxSelection
      components={{ NoRowsOverlay, NoResultsOverlay }}
      componentsProps={{
        pagination: {
          labelRowsPerPage: 'Articles/page',
          rowsPerPageOptions: [5, 10, 20, 50, 100],
        },
      }}
      onSelectionModelChange={(ids) => {
        onRowsSelectionHandler(ids);
      }}
      selectionModel={selectionModel}  
    />
  );
};

I have tried to follow this https://github.com/mui/mui-x/issues/2842 but I really need to keep my own filter and search bar.

I have seen this post with the same problem but no solution How to keep selected checkbox row when we filter the rowData in material UI Datagrid even when its not present in the filtered rowData

like image 226
Elodie Avatar asked Sep 02 '25 13:09

Elodie


1 Answers

Check this answer

You have to use Controlled Row Selection with prop keepNonExistentRowsSelected as described here

<DataGrid
  checkboxSelection
  keepNonExistentRowsSelected
  onRowSelectionModelChange={(newRowSelectionModel) => {
    setRowSelectionModel(newRowSelectionModel);
  }}
  rowSelectionModel={rowSelectionModel}
  {...data}
/>
like image 52
Waqas Ahmed Avatar answered Sep 05 '25 16:09

Waqas Ahmed