Table State
@qui/react-table
has a straightforward state management system for storing and managing the table's state. It allows you to extract specific state elements to integrate with your own state management system. This guide will explain the various methods for interacting with and managing the table's state.
Accessing Table State
No special setup is required for the table state to function. If you do not provide any parameters for state
, initialState
, or any of the on[State]Change
table options, the table will automatically manage its state internally. You can access any part of this internal state through the table.getState()
API.
const table = useReactTable({columns,data,// ...})console.log(table.getState()) // access the entire internal stateconsole.log(table.getState().rowSelection) // access just the row selection state
Initial State
If you only need to customize the initial values for certain states, you don't need to manage the state yourself. Simply set the desired values in the initialState
option of the table instance.
const table = useReactTable({columns,data,initialState: {columnOrder: ["age", "firstName", "lastName"], // customize the initial column ordercolumnVisibility: {id: false, // hide the id column by default},expanded: true, // expand all rows by defaultsorting: [{id: "age",desc: true, // sort by age in descending order by default},],},// ...})
NOTE
Specify each particular state in either initialState or state, but not both. If you provide a state value to both initialState and state, the value in state will overwrite the corresponding value in initialState.
Controlled State
@qui/react-table
allows you to control and manage any or all of the table state within your own state management system. You can achieve this by passing your own state and state management functions to the state
and on[State]Change
table options.
Individual Controlled State
You can control only the state you need easy access to; you do not have to manage the entire table state if it’s unnecessary. It's recommended to control state selectively, based on your specific needs.
To control a particular state, you need to pass both the corresponding state value and the on[State]Change
function to the table instance.
For example, in a "manual" server-side data fetching scenario, you can manage the filtering, sorting, and pagination state within your own state management system. You can ignore other states, like column order or column visibility, if they are not relevant to your API.
const [columnFilters, setColumnFilters] = React.useState([]) //no default filtersconst [sorting, setSorting] = React.useState([{id: "age",desc: true, // sort by age in descending order by default},])const [pagination, setPagination] = React.useState({pageIndex: 0, pageSize: 15})//Use our controlled state values to fetch dataconst tableQuery = useQuery({queryKey: ["users", columnFilters, sorting, pagination],queryFn: () => fetchUsers(columnFilters, sorting, pagination),// ...})const table = useReactTable({columns,data: tableQuery.data,// Override internal state with controlled statestate: {columnFilters,sorting,pagination,},// Use custom state management for column filtersonColumnFiltersChange: setColumnFilters,// Use custom state management for sortingonSortingChange: setSorting,// Use custom state management for paginationonPaginationChange: setPagination,// ...})
Fully Controlled State
Alternatively, you can control the entire table state using the onStateChange
table option. This will hoist the entire table state into your own state management system. However, be cautious with this approach, as raising frequently changing state values, such as columnSizingInfo
, up a component tree might cause performance issues.
A couple of additional steps may be needed to make this work. When using the onStateChange table option, the initial values of the state must include all relevant state values for the features you want to use. You can either manually input all the initial state values or use a constructor in a special way, as demonstrated below.
// create a table instance with default state valuesconst table = useReactTable({columns,data,// Note: `state` values are NOT passed in yet})const [state, setState] = React.useState({// populate the initial state with all the default state values from the table instance...table.initialState,pagination: {pageIndex: 0,pageSize: 15, // optionally customize the initial pagination state.},})// Use the table.setOptions API to merge our fully controlled state onto the table instancetable.setOptions((prev) => ({...prev, // preserve any other options that we have set up abovestate, // our fully controlled state overrides the internal stateonStateChange: setState, // any state changes will be pushed up to our own state management}))
On State Change Callbacks
So far, we have seen that the on[State]Change
and onStateChange
table options can "hoist" table state changes into our own state management. However, there are a few important considerations to keep in mind when using these options.
Callback Parameters
State Change Callbacks MUST have their corresponding state value in the state
option.
Specifying an on[State]Change
callback indicates that the state will be controlled. If you do not provide the corresponding state value, that state will remain "frozen" at its initial value.
const [sorting, setSorting] = React.useState([])// ...const table = useReactTable({columns,data,state: {sorting, // required because we are using `onSortingChange`},onSortingChange: setSorting, // makes the `state.sorting` controlled// ...})
Updaters
Updaters can either be raw values or callback functions (like the useState
setter function).
The on[State]Change
and onStateChange
callbacks function exactly like the setState
function. The updater values can either be a new state value or a callback function that takes the previous state value and returns the new state value.
This means that if you want to add some extra logic to any of the on[State]Change
callbacks, you can do so, but you need to check whether the new incoming updater value is a function or a value.
const [sorting, setSorting] = React.useState([])const [pagination, setPagination] = React.useState({pageIndex: 0, pageSize: 10})const table = useReactTable({columns,data,state: {pagination,sorting,},// syntax 1onPaginationChange: (updater) => {setPagination((old) => {const newPaginationValue =updater instanceof Function ? updater(old) : updater// do something with the new pagination value// ...return newPaginationValue})},// syntax 2onSortingChange: (updater) => {const newSortingValue =updater instanceof Function ? updater(sorting) : updater// do something with the new sorting value// ...setSorting(updater) // normal state update},})
State Types
All complex states in @qui/react-table
have their own TypeScript types that you can import and use. This is useful for ensuring that you are using the correct data structures and properties for the state values you are controlling.