如何在material-react-table v3中用Redux实现rowSelection状态管理?
背景
我正在使用material-react-table v3,组件实现代码如下:
import {MaterialReactTable,useMaterialReactTable} from "material-react-table"; const MaterialTable = ({ columns = [], data = [], selectedState, setSelectedState, isSelectable = false, }: any) => { const table = useMaterialReactTable({ columns, data, enableRowSelection: isSelectable, getRowId: (row) => row.key, onRowSelectionChange: setSelectedState, state: { rowSelection: selectedState }, }); return <MaterialReactTable table={table} />; };
当使用useState传递行选择状态和变更回调时完全正常:
const [materialState, setMaterialState] = useState({}); <MaterialTable selectedState={materialState} setSelectedState={setMaterialState} />
但改用Redux管理状态时,尝试了以下代码:
const filters = useSelector((state) => state.filters); const dispatch = useDispatch(); <MaterialTable selectedState={filters.searchTypeSelection} setSelectedState={(value: any) => dispatch(setSearchTypeSelection(value)) } />
此时出现错误:
A non-serializable value was detected in an action, in the path: payload. Value: old => {
var _opts$selectChildren;
value = typeof value !== 'undefined' ? value : !isSelected;
if (row.getCanSelect() && isSelected === value) {
return old;
… Take a look at the logic that dispatched this action: {type: 'filters/setSearchTypeSelection', payload: ƒ}
我想知道:
- 这个错误的原因是什么?
- 如何正确用Redux实现material-react-table的状态管理?
错误原因
material-react-table的onRowSelectionChange回调可能接收两种类型的参数:
- 直接的行选择状态对象(格式如
{rowId: true/false}) - 函数式更新函数(类似React setState的用法,比如
(prevState) => newState)
当你把setSelectedState设为(value) => dispatch(setSearchTypeSelection(value))时,如果表格传入的是函数式更新参数,就会把这个函数作为payload传给Redux action。而Redux(尤其是Redux Toolkit默认配置)要求action的payload必须是可序列化的值(不能是函数),因此触发了序列化错误。
解决方法
需要在setSelectedState回调中先处理函数式更新的情况,将其转换为最终的状态值后,再dispatch给Redux:
步骤1:修改父组件的状态变更处理逻辑
在使用MaterialTable的父组件中,新增一个处理函数,统一解析行选择状态的更新值:
const filters = useSelector((state) => state.filters); const dispatch = useDispatch(); const handleRowSelectionChange = (updater) => { // 区分直接值和函数式更新,解析出最终状态 const newSelection = typeof updater === 'function' ? updater(filters.searchTypeSelection) : updater; dispatch(setSearchTypeSelection(newSelection)); }; <MaterialTable selectedState={filters.searchTypeSelection} setSelectedState={handleRowSelectionChange} />
步骤2:确保Redux切片状态可序列化
filters切片中的searchTypeSelection必须是普通可序列化对象(比如{row1: true, row2: false}),不能包含函数或其他非序列化值。示例切片代码如下:
import { createSlice } from '@reduxjs/toolkit'; const initialState = { searchTypeSelection: {}, }; const filtersSlice = createSlice({ name: 'filters', initialState, reducers: { setSearchTypeSelection: (state, action) => { state.searchTypeSelection = action.payload; }, }, }); export const { setSearchTypeSelection } = filtersSlice.actions; export default filtersSlice.reducer;
验证逻辑
修改后,无论表格传入的是直接状态对象还是函数式更新,都会先转换为最终的状态值,再传递给Redux action,避免了将函数作为payload的问题,也就不会触发序列化错误了。
内容的提问来源于stack exchange,提问作者Ravishankar Chatterjee

