Managing the tree expand/collapse state
By default, the tree will be rendered with all nodes expanded. This is fine for basic use cases, but as soon as you go into more complex scenarios, you will want to control which nodes are expanded or collapsed.
This is easy to achieve via the defaultTreeExpandState
prop. This is an uncontrolled prop and allows you to initially specify the expand/collapse state of the tree - all subsequent user updates will result in the tree state being updated to match the UI actions.
import { DataSourceApi, InfiniteTableColumn, TreeDataSource, TreeExpandStateValue, TreeGrid, } from '@infinite-table/infinite-react'; import { useState } from 'react'; type FileSystemNode = { id: string; name: string; type: 'folder' | 'file'; extension?: string; mimeType?: string; sizeInKB: number; children?: FileSystemNode[]; }; const columns: Record<string, InfiniteTableColumn<FileSystemNode>> = { name: { field: 'name', header: 'Name', renderTreeIcon: true }, type: { field: 'type', header: 'Type' }, extension: { field: 'extension', header: 'Extension' }, mimeType: { field: 'mimeType', header: 'Mime Type' }, size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' }, }; const defaultTreeExpandState: TreeExpandStateValue = { defaultExpanded: true, collapsedPaths: [ ['1', '10'], ['3', '31'], ], expandedPaths: [['3']], }; export default function App() { const [dataSourceApi, setDataSourceApi] = useState<DataSourceApi<FileSystemNode> | null>(); return ( <> <TreeDataSource onReady={setDataSourceApi} nodesKey="children" primaryKey="id" data={dataSource} defaultTreeExpandState={defaultTreeExpandState} > <div style={{ color: 'var(--infinite-cell-color)', padding: '10px', }} > <button onClick={() => { dataSourceApi!.treeApi.expandAll(); }} > Expand all </button> <button onClick={() => { dataSourceApi!.treeApi.collapseAll(); }} > Collapse all </button> </div> <TreeGrid columns={columns} /> </TreeDataSource> </> ); } const dataSource = () => { const nodes: FileSystemNode[] = [ { id: '1', name: 'Documents', sizeInKB: 1200, type: 'folder', children: [ { id: '10', name: 'Private', sizeInKB: 100, type: 'folder', children: [ { id: '100', name: 'Report.docx', sizeInKB: 210, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '101', name: 'Vacation.docx', sizeInKB: 120, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '102', name: 'CV.pdf', sizeInKB: 108, type: 'file', extension: 'pdf', mimeType: 'application/pdf', }, ], }, ], }, { id: '2', name: 'Desktop', sizeInKB: 1000, type: 'folder', children: [ { id: '20', name: 'unknown.txt', sizeInKB: 100, type: 'file', }, ], }, { id: '3', name: 'Media', sizeInKB: 1000, type: 'folder', children: [ { id: '30', name: 'Music - empty', sizeInKB: 0, type: 'folder', children: [], }, { id: '31', name: 'Videos', sizeInKB: 5400, type: 'folder', children: [ { id: '310', name: 'Vacation.mp4', sizeInKB: 108, type: 'file', extension: 'mp4', mimeType: 'video/mp4', }, ], }, ], }, ]; return Promise.resolve(nodes); };
Understanding the tree expand state
You can specify the expand/collapse state of the tree in two ways:
- With node paths (recommended)
When using node paths, the object should have the following properties:
defaultExpanded
:boolean
- whether the tree nodes are expanded by default or not.collapsedPaths
:string[]
- whendefaultExpanded
istrue
, this is a mandatory prop.expandedPaths
:string[]
- whendefaultExpanded
isfalse
, this is a mandatory prop.
const treeExpandState = {
defaultExpanded: true,
collapsedPaths: [
['1', '10'],
['2', '20'],
['5']
],
expandedPaths: [
['1', '4'],
['5','nested node in 5'],
],
};
- With node ids
When using node ids, the object should have the following properties:
defaultExpanded
:boolean
- whether the tree nodes are expanded by default or not.collapsedIds
:string[]
- whendefaultExpanded
istrue
, this is a mandatory prop.expandedIds
:string[]
- whendefaultExpanded
isfalse
, this is a mandatory prop.
const treeExpandState = {
defaultExpanded: true,
collapsedIds: ['1', '2', '5'],
expandedIds: ['10', '20', 'nested node in 5'],
};
Reacting to user actions
You can listen to the user interactions with the tree by using the onTreeExpandStateChange(treeExpandState, {dataSourceApi, nodePath, nodeState})
callback. This callback is called with the new tree state whenever the user expands or collapses a node.
In addition to this callback, you can also use the following:
Note
The onNodeExpand
and onNodeCollapse
callbacks are called when a node is expanded or collapsed, respectively - either via user interaction or by an API call. However, they will not be called when the expandAll
or collapseAll
methods are called.
Using controlled expand/collapse state
If you want maximum control over the collapse/expand state, you should use the controlled treeExpandState
prop.
This will allow you to own the collapse/expand state entirely - but make sure you use the onTreeExpandStateChange
callback to react to user actions or API calls being made to update the tree state.
import { InfiniteTableColumn, TreeDataSource, TreeExpandStateValue, TreeGrid, } from '@infinite-table/infinite-react'; import { useState } from 'react'; type FileSystemNode = { id: string; name: string; type: 'folder' | 'file'; extension?: string; mimeType?: string; sizeInKB: number; children?: FileSystemNode[]; }; const columns: Record<string, InfiniteTableColumn<FileSystemNode>> = { name: { field: 'name', header: 'Name', renderTreeIcon: true }, type: { field: 'type', header: 'Type' }, extension: { field: 'extension', header: 'Extension' }, mimeType: { field: 'mimeType', header: 'Mime Type' }, size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' }, }; export default function App() { const [treeExpandState, setTreeExpandState] = useState<TreeExpandStateValue>({ defaultExpanded: true, collapsedPaths: [['1', '10'], ['3']], }); return ( <> <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource} treeExpandState={treeExpandState} onTreeExpandStateChange={setTreeExpandState} > <div style={{ color: 'var(--infinite-cell-color)', padding: '10px', }} > <button onClick={() => { setTreeExpandState({ defaultExpanded: true, collapsedPaths: [] }); }} > Expand all </button> <button onClick={() => { setTreeExpandState({ defaultExpanded: false, expandedPaths: [], }); }} > Collapse all </button> <div> Current tree expand state: <pre>{JSON.stringify(treeExpandState, null, 2)}</pre> </div> </div> <TreeGrid columns={columns} /> </TreeDataSource> </> ); } const dataSource = () => { const nodes: FileSystemNode[] = [ { id: '1', name: 'Documents', sizeInKB: 1200, type: 'folder', children: [ { id: '10', name: 'Private', sizeInKB: 100, type: 'folder', children: [ { id: '100', name: 'Report.docx', sizeInKB: 210, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '101', name: 'Vacation.docx', sizeInKB: 120, type: 'file', extension: 'docx', mimeType: 'application/msword', }, { id: '102', name: 'CV.pdf', sizeInKB: 108, type: 'file', extension: 'pdf', mimeType: 'application/pdf', }, ], }, ], }, { id: '2', name: 'Desktop', sizeInKB: 1000, type: 'folder', children: [ { id: '20', name: 'unknown.txt', sizeInKB: 100, type: 'file', }, ], }, { id: '3', name: 'Media', sizeInKB: 1000, type: 'folder', children: [ { id: '30', name: 'Music', sizeInKB: 0, type: 'folder', children: [], }, { id: '31', name: 'Videos', sizeInKB: 5400, type: 'folder', children: [ { id: '310', name: 'Vacation.mp4', sizeInKB: 108, type: 'file', extension: 'mp4', mimeType: 'video/mp4', }, ], }, ], }, ]; return Promise.resolve(nodes); };
Note
When using controlled treeExpandState
, you no longer need to use API calls.
When you need to expand all nodes, simply set the treeExpandState
to {defaultExpanded: true, collapsedPaths: []}
.
When you need to collapse all nodes, simply set the treeExpandState
to {defaultExpanded: false, expandedPaths: []}
.