ReactJS and Material UI TreeView: can I use a JSON/array of object to populate the TreeView?
The TreeView
component doesn't have anything built-in for this, but it is fairly straightforward to create re-usable code to provide this functionality for a given data structure.
Here is one way to do it:
import React from "react";import ReactDOM from "react-dom";import TreeView from "@material-ui/lab/TreeView";import TreeItem from "@material-ui/lab/TreeItem";import ExpandMoreIcon from "@material-ui/icons/ExpandMore";import ChevronRightIcon from "@material-ui/icons/ChevronRight";import { sampleFromStackOverflowQuestion, seasons } from "./sampleData";const getTreeItemsFromData = treeItems => { return treeItems.map(treeItemData => { let children = undefined; if (treeItemData.children && treeItemData.children.length > 0) { children = getTreeItemsFromData(treeItemData.children); } return ( <TreeItem key={treeItemData.id} nodeId={treeItemData.id} label={treeItemData.name} children={children} /> ); });};const DataTreeView = ({ treeItems }) => { return ( <TreeView defaultCollapseIcon={<ExpandMoreIcon />} defaultExpandIcon={<ChevronRightIcon />} > {getTreeItemsFromData(treeItems)} </TreeView> );};function App() { return ( <div className="App"> <DataTreeView treeItems={sampleFromStackOverflowQuestion} /> <br /> <DataTreeView treeItems={seasons} /> </div> );}const rootElement = document.getElementById("root");ReactDOM.render(<App />, rootElement);
DataTreeView
and getTreeItemsFromData
could be moved into a separate file and then imported in order to reuse them in multiple components.
Here is a Typescript version:
import * as React from "react";import TreeView from "@material-ui/lab/TreeView";import TreeItem from "@material-ui/lab/TreeItem";import ExpandMoreIcon from "@material-ui/icons/ExpandMore";import ChevronRightIcon from "@material-ui/icons/ChevronRight";import { sampleFromStackOverflowQuestion, seasons, TreeItemData} from "./sampleData";const getTreeItemsFromData = (treeItems: TreeItemData[]) => { return treeItems.map(treeItemData => { let children = undefined; if (treeItemData.children && treeItemData.children.length > 0) { children = getTreeItemsFromData(treeItemData.children); } return ( <TreeItem key={treeItemData.id} nodeId={treeItemData.id} label={treeItemData.name} children={children} /> ); });};interface DataTreeViewProps { treeItems: TreeItemData[];}function DataTreeView({ treeItems }: DataTreeViewProps) { return ( <TreeView defaultCollapseIcon={<ExpandMoreIcon />} defaultExpandIcon={<ChevronRightIcon />} > {getTreeItemsFromData(treeItems)} </TreeView> );}export default function App() { return ( <div className="App"> <DataTreeView treeItems={sampleFromStackOverflowQuestion} /> <br /> <DataTreeView treeItems={seasons} /> </div> );}
Here is one more way to day as mentioned in the Material UI Docs.
import React from 'react';import { makeStyles } from '@material-ui/core/styles';import TreeView from '@material-ui/lab/TreeView';import ExpandMoreIcon from '@material-ui/icons/ExpandMore';import ChevronRightIcon from '@material-ui/icons/ChevronRight';import TreeItem from '@material-ui/lab/TreeItem';import _ from 'lodash';const useStyles = makeStyles({ root: { height: 216, flexGrow: 1, maxWidth: 400, },});export default function ControlledTreeView(props) { const classes = useStyles(); const [expanded, setExpanded] = React.useState([]); const [selected, setSelected] = React.useState([]); const [searchValue, setSearchValue] = React.useState(''); const [stateData, setStateData] = React.useState([]); React.useEffect( () => { const { data } = props; setStateData(data); }, [props], ); const handleToggle = (event, nodeIds) => { setExpanded(nodeIds); }; const handleSelect = (event, nodeIds) => { setSelected(nodeIds); }; const onSearch = (search) => { setSearchValue(search); console.log(searchTree(search, props.data)); }; function searchTree(str, data) { const searchStr = str.toLowerCase(); // Only return the entries that contain a matched value return _.filter(data, (datum) => { // Check if name matches return _.includes(datum.name, searchStr) || _.some(datum.activities, (activity) => { return _.entries(activity.routines).some(([routine, {details}]) => { // Check if dynamic routine matches or details return _.includes(routine, searchStr) || _.includes(details, searchStr); }); }); }); }; const renderTree = (nodes) => ( <TreeItem key={nodes.id} nodeId={nodes.id} label={nodes.name}> {Array.isArray(nodes.children) ? nodes.children.map((node) => renderTree(node)) : null} </TreeItem> ); return ( <div> <TreeView className={classes.root} defaultCollapseIcon={<ExpandMoreIcon />} defaultExpanded={['root']} defaultExpandIcon={<ChevronRightIcon />} > {renderTree(stateData)} </TreeView> </div> );}
And you can call this component as<CustomizedTreeView data={TreeViewData} />
where your TreeViewData
will be
const TreeViewData = { id: 'root', name: 'Documents', children: [ { id: '1', name: 'Report', children: [ { id: '2', name: 'PDF', }, ], }, { id: '3', name: 'Files', children: [ { id: '4', name: 'Excel', }, ], }, { id: '5', name: 'Programs', children: [ { id: '6', name: 'Codes', }, { id: '7', name: 'Data', }, ], }, ],};