State Management Guide
Material React Table does not try to hide any of its internal state from you. You can store your own reference to the entire table instance wherever you need to, initialize state with custom initial values, or manage individual states yourself as you discover the need to have access to them.
This is all optional, of course. If you do not need access to some of the state, you do not need to do anything and it will just automatically be managed internally.
See the State Options API Docs for more information on which states are available for you to manage.
Relevant Props
# | Prop Name | Type | Default Value | More Info Links | |
---|---|---|---|---|---|
1 |
| Table State Management Guide | |||
2 |
| Table State Management Guide | |||
3 |
| ||||
Access the Underlying Table Instance Reference
Before jumping in and managing a bunch of state yourself, you should first see if you actually need to.
You can store a reference to the underlying table instance by using the tableInstanceRef
prop. This is a mutable ref object that will be populated with the table instance once the table has been initialized internally. This reference can be very useful, especially if you need it for components that are rendered outside of <MaterialReactTable />
.
//must initialize with null to make TS happyconst tableInstanceRef = useRef<MRT_TableInstance<YourDataType>>(null);const someEventHandler = (event) => {console.info(tableInstanceRef.current?.getRowModel().rows); //example - get access to all page rows in the tableconsole.info(tableInstanceRef.current?.getSelectedRowModel()); //example - get access to all selected rows in the tableconsole.info(tableInstanceRef.current?.getState().sorting); //example - get access to the current sorting state};return (<div><ExternalButton onClick={someEventHandler}>Export or Something</ExternalButton><MaterialReactTablecolumns={columns}data={data}tableInstanceRef={tableInstanceRef}/></div>);
The table instance is the same object that you will also see as a provided parameter in many of the other callback functions throughout Material React Table, such as all the render...
props or the Cell
or Header
render overrides in the column definition options.
const tableInstanceRef = useRef(null);const columns = useMemo(() => [{Header: 'Name',accessor: 'name',Cell: ({ cell, table }) => <span>{cell.getValue()}</span>,//The `table` parameter from the Cell option params and the `tableInstanceRef.current` are the same object},],[],);return (<MaterialReactTablecolumns={columns}data={data}tableInstanceRef={tableInstanceRef}renderTopToolbarCustomActions={({ table }) => {//The `table` parameter and the `tableInstanceRef.current` are the same objectreturn <Button>Button</Button>;}}/>);
Populate Initial State
If all you care about is setting parts of the initial or default state when the table mounts, then you may be able to specify that state in the initialState
prop and not have to worry about managing the state yourself.
For example, let's say you do not need access to the showColumnFilters
state, but you want to set the default value to true
when the table mounts. You can do that with the initialState
prop:
<MaterialReactTablecolumns={columns}data={data}initialState={{showColumnFilters: true, //show filters by defaultsorting: [{ id: 'name', desc: false }], //sort by name ascending by default}}/>
Note: If you use both
initialState
andstate
, the state initializer instate
prop will take precedence and overwrite the same state values ininitialState
. So just use eitherinitialState
orstate
, not both for the same state.
Manage Individual States as Needed
It is pretty common to need to manage certain state yourself, so that you can react to changes in that state, or have easy access to it when sending it to an API.
You can pass in any state that you are managing yourself to the state
prop, and it will be used instead of the internal state. Each state property option also has a corresponding on[StateName]Change
callback that you can use set/update your managed state as it changes internally in the table.
For example, let's say you need to store the pagination state in a place where you can easily access it in order to use it in parameters for an API call.
const [pagination, setPagination] = useState({pageIndex: 0,pageSize: 15, //set different default page size by initializing the state here});//see example at bottom of page for alternatives to useEffect hereuseEffect(() => {//do something when the pagination state changes}, [pagination]);return (<MaterialReactTablecolumns={columns}data={data}state={{ pagination }}onPaginationChange={setPagination}/>);
Here is another full example for how you might manage the rowSelection
state yourself:
First Name | Last Name | Age | Address | City | State | |
---|---|---|---|---|---|---|
Dylan | Murray | 22 | 261 Erdman Ford | East Daphne | Kentucky | |
Raquel | Kohler | 18 | 769 Dominic Grove | Columbus | Ohio |
1import React, { useEffect, useMemo, useState } from 'react';2import MaterialReactTable, {3 MRT_ColumnDef,4 MRT_RowSelectionState,5} from 'material-react-table';67const data = [8 {9 userId: '3f25309c-8fa1-470f-811e-cdb082ab9017', //we'll use this as a unique row id10 firstName: 'Dylan',11 lastName: 'Murray',12 age: 22,13 address: '261 Erdman Ford',14 city: 'East Daphne',15 state: 'Kentucky',16 }, //data definitions...27];2829const Example = () => {30 const columns = useMemo(31 //column definitions...60 );6162 //optionally, you can manage the row selection state yourself63 const [rowSelection, setRowSelection] = useState<MRT_RowSelectionState>({});6465 useEffect(() => {66 //do something when the row selection changes...67 console.info({ rowSelection });68 }, [rowSelection]);6970 return (71 <MaterialReactTable72 columns={columns}73 data={data}74 enableRowSelection75 getRowId={(row) => row.userId} //give each row a more useful id76 onRowSelectionChange={setRowSelection} //connect internal row selection state to your own77 state={{ rowSelection }} //pass our managed row selection state to the table to use78 />79 );80};8182export default Example;83
Add Side Effects in Set State Callbacks
In React 18 and beyond, it is becoming more discouraged to use useEffect
to react to state changes, because in React Strict Mode (and maybe future versions of React), the useEffect hook may run twice per render. Instead, more event driven functions are recommended to be used. Here is an example for how that looks here. The callback signature for the on[StateName]Change
functions is a bit strange, so pay attention to the example below.
const [pagination, setPagination] = useState({pageIndex: 0,pageSize: 15,});const handlePaginationChange = (updater: Updater<PaginationState>) => {//Updater is a type from @tanstack/react-table//TanStack Table requires this strange syntax since updater can be a function to update the state or the new state itselfsetPagination((prevPagination) => updater instanceof Function ? updater(prevPagination) : updater);//put more code for your side effects here, guaranteed to only run once, even in React Strict Mode}return (<MaterialReactTablecolumns={columns}data={data}state={{ pagination }}onPaginationChange={handlePaginationChange}/>);