Fluent UI React:DetailsList的onSelectionChanged不触发及分页选中专属问题
问题描述
我实现了自定义分页功能,需求是分页切换时保留选中状态——用户在第1页选中项1,切换到第2页再切回第1页时,项1需保持选中。但目前选中/取消选中项时,onSelectionChanged事件始终不触发,调试器仅在加载时触发一次,后续操作无响应。相关代码如下:
const RelationalDataList: React.FC<IRelationalDataListProps> = (props) => { //... const handleSelectionChanged = () => { debugger; }; useEffect(() => { setSelection(() => { // TRY WITH NEW SELECTION ?? if (!selectionProps) { return undefined; } const sel = new Selection({ selectionMode: selectionProps.selectMultiple ? SelectionMode.multiple : SelectionMode.single, onSelectionChanged: handleSelectionChanged, canSelectItem: () => { return true; }, items: [...list], getKey: (item) => { return item[getPrimaryKeyName(entityDefinition)]; }, onItemsChanged: () => { } }); selectionProps.selectedIds.forEach((selectedId) => { sel.setKeySelected(selectedId, true, false); }); return sel; }); }, []); // only one instance //somewhere down the line: const listComponent = useMemo(() => { const listElement = <DetailsList selectionMode={selectionProps ? (selectionProps.selectMultiple ? SelectionMode.multiple : SelectionMode.single) : SelectionMode.none} items={list} columns={columns} selection={selection} selectionPreservedOnEmptyClick={true} getKey={(item) => { return item[pkPropName]; }} layoutMode={DetailsListLayoutMode.fixedColumns} onRenderCheckbox={(defaultProps, defaultRender) => { return <Stack styles={{ root: { paddingTop: '11px' } }}>{defaultRender && defaultRender(defaultProps)}</Stack>; }} styles={...} />; if (!maxHeight) { return <AutoSizer> {({ height, width }) => { return <Stack className='list-sizer' styles={{ root: { height, width } }}> {listElement} </Stack>; }} </AutoSizer>; } return listElement; }, [selectionProps, list, columns, selection, maxHeight, pkPropName]);
解决方案
核心问题分析
- Selection实例仅初始化一次:
useEffect依赖为空数组,组件挂载后只会创建一次Selection实例,分页切换时list(当前页数据)更新,但旧实例绑定的还是初始页的items,导致事件无法触发。 - 选中状态未全局同步:当前代码没有把选中状态同步到父组件,分页切换后无法保留跨页的选中记录。
1. 让Selection实例随分页更新
修改useEffect的依赖项,把list和selectionProps加进去,这样分页切换或选中配置变化时,重新创建绑定当前页数据的Selection实例:
useEffect(() => { if (!selectionProps) { setSelection(undefined); return; } const sel = new Selection({ selectionMode: selectionProps.selectMultiple ? SelectionMode.multiple : SelectionMode.single, onSelectionChanged: handleSelectionChanged, canSelectItem: () => true, items: [...list], getKey: (item) => item[getPrimaryKeyName(entityDefinition)], onItemsChanged: () => {} }); // 只标记当前页中已被选中的项 const currentPageSelectedIds = selectionProps.selectedIds.filter(id => list.some(item => item[getPrimaryKeyName(entityDefinition)] === id) ); currentPageSelectedIds.forEach((selectedId) => { sel.setKeySelected(selectedId, true, false); }); setSelection(sel); }, [list, selectionProps]); // 依赖更新时重建实例
2. 同步选中状态到父组件
在handleSelectionChanged中,把当前选中的ID集合传递给父组件保存,这样跨页切换时能保留所有选中记录:
const handleSelectionChanged = () => { debugger; // 获取当前所有选中的key const selectedKeys = selection?.getSelectedKeys() || new Set(); // 调用父组件的回调更新全局选中ID selectionProps.onSelectedIdsChanged?.(Array.from(selectedKeys)); };
注意:需要确保
IRelationalDataListProps定义了onSelectedIdsChanged回调,用于父组件接收选中状态。
3. 确认DetailsList绑定最新实例
listComponent的useMemo已经包含selection作为依赖,这部分不用改,确保Selection实例更新时,DetailsList能重新渲染并绑定新实例。
4. 父组件维护全局选中状态
父组件需要保存一个全局的selectedIds数组,当子组件(RelationalDataList)触发onSelectedIdsChanged时更新这个数组,分页切换时把这个数组传给子组件的selectedIds属性,就能实现跨页保留选中状态。
内容的提问来源于stack exchange,提问作者ThatBrianDude
相关产品推荐
相关产品推荐

