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

3

6

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;
        } 
Janettejaneva answered 7/12, 2022 at 18:30 Comment(0)
M
19

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.

Marshallmarshallese answered 1/1, 2023 at 20:28 Comment(1)
This was immensely helpful and much better explained than the docs! Looks like the type for Updater is still strange, added a check in my version: ``` const setPagination: OnChangeFn<PaginationState> = (updater) => { if (typeof updater === 'function') { const nextState = updater(pagination) setPaginationState(nextState) } } ```Shemikashemite
E
3

For those who just want to get the new page index and size without additional React.useState.

Since the onPaginationChange callback takes an updater as an argument, you can just call it to get the new value.

onPaginationChange: (updater) => {
  // make sure updater is callable (to avoid typescript warning)
  if (typeof updater !== "function") return;

  const newPageInfo = updater(table.getState().pagination);

  console.log(table.getState().pagination);
  // output: {pageIndex: 0, pageSize: 30}

  console.log(newPageInfo);
  // output: {pageIndex: 1, pageSize: 30}
}

You get code completion as well on VSCode after typing newPageInfo.

Tested on react-table v8.

Edora answered 6/2 at 9:35 Comment(0)
C
0

Add Callback to handle pagination changes received in props

export function DataTable({
  columns,
  data,
  onPaginationChange,//Callback
}) {

const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
  });

const currentPageIndex = table.getState().pagination.pageIndex;

useEffect(() => {
  onPaginationChange && onPaginationChange(currentPageIndex);
}, [currentPageIndex]);

Use the useEffect hook to listen for changes in the current page index and trigger the onPaginationChange callback when it changes.

Usage

<DataTable
  columns={columns}
  data={event.data}
  onPaginationChange={console.log}
/>
Corley answered 12/11, 2023 at 6:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.