July 24, 2023
O. Wolfson
In this tutorial, we will learn how to convert a flat data structure into a hierarchical tree view with collapsible nodes using React. This can be useful when displaying nested data, such as a category hierarchy or a folder structure.
Example app deployed at Vercel.
JavaScript // This is your initial flat data structure
const data: Item[] = [
{ name: "Electronics", id: "1", parent: null },
{ name: "Furniture", id: "2", parent: null },
{ name: "Smartphones", id: "3", parent: "1" },
{ name: "Laptops", id: "4", parent: "1" },
{ name: "Chairs", id: "5", parent: "2" },
{ name: "Gaming Laptops", id: "6", parent: "4" },
{ name: "iPhone", id: "7", parent: "3" },
{ name: "MacBook Pro", id: "8", parent: "4" },
{ name: "Office Chairs", id: "9", parent: "5" },
{ name: "Dining Chairs", id: "10", parent: "5" },
{ name: "Recliners", id: "11", parent: "5" },
{ name: "Gaming Chairs", id: "12", parent: "5" }, // Corrected placement
];
Basic knowledge of React and JavaScript.
Create a new React project using Create React App or your preferred method.
Define the structure of each item in your data. For example, each item might have a name, unique ID, and a reference to its parent item (if it's a child item).
JavaScript// Define the structure of an item in your data
type Item = {
name: string;
id: string;
parent: string | null;
};
Create a function that converts the flat data structure into a hierarchical nested structure. We'll call this function buildNestedStructure
. It will iterate through the flat data and construct a map of items with their children.
JavaScripttype NestedItem = Item & { children: NestedItem[] };
// This function converts an array of Items into a hierarchical structure of NestedItems
const buildNestedStructure = (items: Item[]): NestedItem[] => {
const itemMap: { [id: string]: NestedItem } = {};
// Function to create a NestedItem from an Item
const createNestedItem = (item: Item): NestedItem => ({
...item,
children: [],
});
// Loop through each item in the provided items array
items.forEach((item) => {
// For each item, create a new NestedItem by calling the createNestedItem function,
// and add it to the itemMap object with its ID as the key.
// This will create a new object that contains all the properties of the original item,
// plus an empty children array.
// It's worth noting that at this point, all NestedItems in the itemMap have an empty children array.
itemMap[item.id] = createNestedItem(item);
// Next, check if the current item has a parent.
// If it does, it means it should be nested inside another item.
if (item.parent) {
// If the item has a parent, add it to the children array of its parent NestedItem in the itemMap.
// This is where the hierarchical nesting happens.
// Note that since we're directly modifying the itemMap objects,
// the same changes will be reflected in all places where these objects are referenced.
// So when we added this item to its parent's children array,
// it also appeared in the children array of the same item in the itemMap.
itemMap[item.]..(itemMap[item.]);
}
});
items.( !item.).( itemMap[item.]);
};
Create a React component to render the nested structure. This component will be recursive, meaning it will render itself for each child item. We'll call this component RenderItem
.
JavaScript// This component takes a NestedItem and a depth, and renders the item and its children
const RenderItem: React.FC<{ item: NestedItem; depth: number }> = ({
item,
depth,
}) => {
const [isExpanded, setIsExpanded] = useState(true);
const handleToggle = () => {
setIsExpanded(!isExpanded);
};
const paddingStyle = { paddingLeft: `${depth * 20}px` };
return (
<div style={paddingStyle}>
{hasChildren && (
<button onClick={handleToggle}>
{isExpanded ? "▼" : "►"} {item.name}
</button>
)}
{isExpanded &&
item.children.map((child) => (
<RenderItem key={child.id} item={child} depth={depth + 1} />
))}
</div>
);
};
Add the ability to collapse and expand nodes in the tree view. We'll use React's useState
hook to track the expanded state of each node. When the user clicks on a node with children, it will toggle between expanded and collapsed states.
JavaScript// Check if the item has children or not
const hasChildren = item.children.length > 0;
return (
<div style={paddingStyle}>
{hasChildren ? (
<button onClick={handleToggle}>
{isExpanded ? "▼" : "►"} {item.name}
</button>
) : (
<div>{item.name}</div>
)}
{isExpanded &&
item.children.map((child) => (
<RenderItem key={child.id} item={child} depth={depth + 1} />
))}
</div>
);
In the main component of your app, use the buildNestedStructure
function to convert your flat data into a nested structure. Then, render the top-level items using the RenderItem
component, passing the nested structure as props.
JavaScriptexport default function MainComponent() {
// This is your initial flat data structure
const data: Item[] = [
// ... Your data here ...
];
// Build the nested structure from your flat data
const nestedData = buildNestedStructure(data);
// Render the top-level items, passing a depth of 0
return (
<div>
{nestedData.map((item) => (
<RenderItem key={item.id} item={item} depth={0} />
))}
</div>
);
}
Congratulations! You've successfully created a collapsible tree view from a flat data structure using React. This allows you to display nested data in a user-friendly and organized way, with the ability to expand and collapse nodes as needed.
Here is the full code for reference.
This tutorial provides a basic example of how to create a collapsible tree view in React. Depending on your project's complexity and requirements, you may need to adjust and customize the implementation accordingly.