typescriptredux-toolkitreact-table

TanStack table | How can I use redux action in onPaginationChange instead of setPaginationState in the example


Using this example
https://tanstack.com/table/v8/docs/examples/react/pagination-controlled
How can I use https://tanstack.com/table/v8/docs/api/features/pagination#onpaginationchange if I need to dispatch my redux action instead of useState - setPaginationState?

onPaginationChange: state => dispatch(browseItemModalActions.setPagination(state))

getting this error in console:

`react_devtools_backend.js:4012 A non-serializable value was detected in an action, in the path: `payload`. Value: old => {
          let newState = functionalUpdate(updater, old);
          return newState;
        } 

Solution

  • This is an interesting question!

    Based on the way that the example uses onPaginationChange: setPagination you might think that the onPaginationChange function gets called with the new state, but it does not.

    Your onPaginationChange function actually gets called with an "updater" function which takes the previous state and returns the new state. This works with React setState functions because they support functional updates where you define your new state as a function of the previous state.

    In other words, when you see this:

    onPaginationChange: setPagination,
    

    You think it's doing this:

    onPaginationChange: (state) => setPagination(state),
    

    But it's actually doing this:

    onPaginationChange: (updater) => setPagination(prevState => updater(prevState)),
    

    Here's the relevant part of the react-table source code:

    setPagination: updater => {
      const safeUpdater: Updater<PaginationState> = old => {
        let newState = functionalUpdate(updater, old)
    
        return newState
      }
    
       return table.options.onPaginationChange?.(safeUpdater)
    },
    

    GitHub Source


    It is not obvious or easy to use this updater function in a Redux action. Redux reducers are "updaters" of their own, and the actions must take raw data.

    Ultimately, you may need to think about whether it really makes sense to store this data in Redux. I do have some workarounds but they have their limitations.

    1. You can keep the pagination state in the component and sync changes back to Redux with a useEffect hook, but this is a one-way syncing and won't work if changes to the Redux pagination state can come from other actions.
    const reduxPagination = useSelector((state) => state.counter.pagination);
    
    const [pagination, setPagination] = React.useState(reduxPagination);
    
    const { pageIndex, pageSize } = pagination;
    
    const dispatch = useDispatch();
    
    useEffect(() => {
      dispatch(browseItemModalActions.setPagination(pagination));
    }, [pagination, dispatch]);
    
    onPaginationChange: setPagination,
    
    1. You can apply the updater to the previously-selected state from Redux and dispatch the updated value.
    const pagination = useSelector((state) => state.counter.pagination);
    
    const dispatch = useDispatch();
    
    const table = useReactTable({
      state: {
        pagination
      },
      onPaginationChange: (updater) => {
        const nextState = updater(pagination);
        dispatch(browseItemModalActions.setPagination(nextState));
      },
      manualPagination: true,
    ...
    

    But now my head is exploding because I'm getting a TypeScript error telling me that updater could be either a function or a value...but we just looked at the source code and it's always a function! So I don't know what to make of this anymore. It makes me wonder if the react-table source code is a mistake and the callback is meant to allow plain data values.