Kendo-React表格虚拟滚动与行选择功能兼容问题求助
Kendo React Grid 虚拟滚动与行选择(单选+全选)兼容方案
问题分析
当前实现存在几个核心问题,导致虚拟滚动和行选择无法正常兼容:
- 虚拟滚动模式下,
data传递了完整的dataState映射数组,破坏了虚拟滚动仅渲染视口内数据的优化逻辑,同时每次选择状态变化都会重新遍历全量数据,性能低下。 onSelectionChange依赖selectedState,导致每次选择变化都会重新创建回调函数,引发不必要的组件重渲染。- 全选逻辑基于当前
dataState判断,但虚拟滚动下dataState通常是分页加载的部分数据,无法正确反映全部数据的选中状态。
解决方案
1. 恢复虚拟滚动的正确数据传递
虚拟滚动模式下,Grid仅需要当前视口范围内的数据,直接传递分页后的切片数据即可,无需给每个数据项附加选中字段。
2. 优化选中状态管理
使用函数式更新维护selectedState,减少useCallback的依赖项,避免不必要的重渲染。
3. 修正全选逻辑
新增selectAll状态区分“全选当前页”和“选中全部”,同时在表头选择时处理两种场景。
修改后的完整代码
const [selectedState, setSelectedState] = React.useState({}); const [selectAll, setSelectAll] = React.useState(false); const onSelectionChange = React.useCallback( (event) => { // 使用函数式更新,避免依赖selectedState setSelectedState(prevState => getSelectedState({ event, selectedState: prevState, dataItemKey: DATA_ITEM_KEY, }) ); // 手动选择行时取消全选状态 setSelectAll(false); }, [] // 空依赖数组,确保回调不会频繁重建 ); const onHeaderSelectionChange = React.useCallback( (event) => { const isSelectAll = event.value; setSelectAll(isSelectAll); if (isSelectAll) { // 全选当前页数据 const newSelectedState = {...selectedState}; dataState.slice(page.skip, page.skip + pageSize).forEach(item => { const id = idGetter(item); newSelectedState[id] = true; }); setSelectedState(newSelectedState); } else { // 取消选中当前页数据 const newSelectedState = {...selectedState}; dataState.slice(page.skip, page.skip + pageSize).forEach(item => { const id = idGetter(item); delete newSelectedState[id]; }); setSelectedState(newSelectedState); } }, [selectedState, page.skip, pageSize, dataState] ); // 自定义行选中状态判断 const isRowSelected = (item) => { const id = idGetter(item); return selectAll || !!selectedState[id]; }; return ( <Grid style={{ height: "60vh" }} // 虚拟滚动仅传递当前页数据 data={dataState.slice(page.skip, page.skip + pageSize)} pageSize={pageSize} total={total} skip={page.skip} scrollable={'virtual'} rowHeight={50} onPageChange={pageChange} cellRender={loadingCell} dataItemKey={DATA_ITEM_KEY} selectable={{ enabled: true, drag: false, cell: false, mode: "multiple", }} onSelectionChange={onSelectionChange} onHeaderSelectionChange={onHeaderSelectionChange} // 自定义行选中状态,无需依赖selectedField rowProps={(item) => ({ selected: isRowSelected(item) })} > <GridColumn locked={true} width="50" // 表头选中状态:当前页所有行都被选中时为true headerSelectionValue={ dataState.slice(page.skip, page.skip + pageSize).every(item => isRowSelected(item)) } // 自定义选择列单元格 cell={(props) => ( <input type="checkbox" checked={isRowSelected(props.dataItem)} onChange={(e) => { const id = idGetter(props.dataItem); setSelectedState(prev => ({ ...prev, [id]: e.target.checked })); setSelectAll(false); }} /> )} /> <GridColumn field="Action" width="102" locked={true} cell={MyCustomCell} /> <GridColumn field="Index" title="Index" width="75" /> <GridColumn field="stone_id" title="Stone ID" width="75" /> <GridColumn field="cert_no" title="Certificate No" width="105" /> </Grid> );
关键修改点说明
- 虚拟滚动数据传递:恢复
data={dataState.slice(page.skip, page.skip + pageSize)},确保Grid只渲染当前视口的行,维持虚拟滚动的性能优势。 - 选中状态优化:
onSelectionChange使用函数式更新setSelectedState(prevState => ...),移除对selectedState的依赖,避免回调频繁重建。 - 全选逻辑修正:新增
selectAll状态,表头选择时仅操作当前页的选中状态,同时通过isRowSelected函数统一判断行选中状态。 - 自定义选择列:手动实现选择列的复选框,确保选中状态正确同步,无需给数据项附加选中字段。
内容的提问来源于stack exchange,提问作者hasti kukadiya
相关产品推荐
相关产品推荐

