如何在MUI X Data Grid搜索/筛选时保留行选中状态
解决MUI X DataGrid筛选/搜索后选中状态丢失问题
问题背景
使用MUI X DataGrid展示文章列表,支持单行/多行选中,已实现自定义搜索和筛选功能,但执行搜索或筛选操作后,之前的选中状态会丢失——原因是筛选后不在结果集中的选中行被自动取消选中。
解决方案核心思路
不再仅依赖当前展示的行维护选中状态,而是在父组件维护一个全局的选中ID集合,这个集合不受筛选/搜索操作影响。筛选后DataGrid只需要渲染当前结果集中的选中项,但全局选中ID保留所有已选记录,恢复筛选时自动还原所有选中状态。
修改步骤及代码调整
1. 父组件SelectArticle修改
添加全局选中ID状态,传递给子组件用于同步:
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); // 新增:维护全局选中ID集合 const [selectedRowIds, setSelectedRowIds] = React.useState([]); 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} // 传递全局选中状态及更新方法 selectedRowIds={selectedRowIds} setSelectedRowIds={setSelectedRowIds} /> </Paper> </div> ); };
2. 选择组件SelectionArticle修改
基于全局选中ID更新DataGrid的选中状态,同时同步react-hook-form的字段值:
import { DataGrid } from '@mui/x-data-grid'; import React, { useEffect } from 'react'; import { Stack } from 'react-bootstrap'; import { useFieldArray } from 'react-hook-form'; export const SelectionArticle = ({ control, setValue, rows, watch, selectedRowIds, setSelectedRowIds }) => { const entityConcerned = watch('OffersInfos.entityConcerned'); const { replace } = useFieldArray({ control, name: 'Articles', }); // 从全局选中ID获取当前展示行中的选中项,用于DataGrid的selectionModel const selectionModel = rows.filter(row => selectedRowIds.includes(row.id)).map(row => row.id); // 当全局选中ID变化时,同步更新react-hook-form的Articles字段 useEffect(() => { const selectedArticles = selectedRowIds.map(id => rows.find(row => row.id === id)).filter(Boolean); replace(selectedArticles); }, [selectedRowIds, rows, replace]); 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) => { // 更新全局选中ID集合 setSelectedRowIds(ids); }; 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> ); } 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} /> ); };
原理说明
- 父组件的
selectedRowIds存储所有被选中行的ID,不受筛选/搜索影响,即使行不在当前展示列表中,ID也会被保留。 - DataGrid的
selectionModel只渲染当前展示行中属于selectedRowIds的项,这样筛选后仍能看到当前结果里的选中状态。 - 当筛选/搜索取消后,所有之前选中的行(包括原本不在结果集的)会重新显示,并且选中状态自动恢复。
内容的提问来源于stack exchange,提问作者Elodie
相关产品推荐
相关产品推荐

