Interactive tree illustration representing he-tree-react functionality

Cultivating React Forests: Unleashing the Power of he-tree-react

The Orange Cat
The Orange Cat

In the ever-growing forest of React components, he-tree-react stands tall as a robust and flexible solution for implementing draggable and sortable tree structures. This powerful library empowers developers to create dynamic, interactive tree components with ease, making it an invaluable tool for building complex user interfaces that require hierarchical data representation.

Rooting Your Project with He-Tree-React

Before we dive into the rich features of he-tree-react, let’s get it planted in your project. You can easily install the library using your preferred package manager:

Using npm

npm install he-tree-react

Using yarn

yarn add he-tree-react

Once installed, you’re ready to start branching out with he-tree-react in your React applications.

Planting the Seeds: Basic Usage

Let’s start by creating a simple tree component using he-tree-react. The library provides a hook called useHeTree that handles the core functionality of the tree.

Creating a Basic Tree

Here’s a basic example of how to set up a tree with flat data:

import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function SimpleTree() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setData] = useState(() => sortFlatData([
    { id: 1, parent_id: null, name: "Root" },
    { id: 2, parent_id: 1, name: "Branch 1" },
    { id: 3, parent_id: 1, name: "Branch 2" },
    { id: 4, parent_id: 2, name: "Leaf 1" },
  ], keys));

  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setData,
    renderNode: ({ node }) => <div>{node.name}</div>,
  });

  return <div>{renderTree()}</div>;
}

In this example, we’re using flat data to represent our tree structure. The sortFlatData function ensures that our data is properly ordered for rendering. The useHeTree hook takes care of the tree logic, while we provide a simple renderNode function to display each node.

Branching Out: Advanced Features

Now that we’ve got our basic tree growing, let’s explore some of the more advanced features that make he-tree-react truly shine.

Drag and Drop Functionality

One of the standout features of he-tree-react is its built-in drag and drop support. Let’s enhance our tree to allow for node reordering:

import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function DraggableTree() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setData] = useState(() => sortFlatData([
    { id: 1, parent_id: null, name: "Root" },
    { id: 2, parent_id: 1, name: "Branch 1" },
    { id: 3, parent_id: 1, name: "Branch 2" },
    { id: 4, parent_id: 2, name: "Leaf 1" },
  ], keys));

  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setData,
    renderNode: ({ node, draggable }) => (
      <div>
        <span draggable={draggable}>🔄</span> {node.name}
      </div>
    ),
  });

  return <div>{renderTree()}</div>;
}

By adding a draggable handle to each node, we’ve enabled users to reorder the tree structure intuitively. The onChange prop ensures that our data state is updated when nodes are moved.

Expandable Nodes

Trees often contain nested data that we want to show or hide. Let’s implement expandable nodes:

import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

export default function ExpandableTree() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setData] = useState(() => sortFlatData([
    { id: 1, parent_id: null, name: "Root" },
    { id: 2, parent_id: 1, name: "Branch 1" },
    { id: 3, parent_id: 1, name: "Branch 2" },
    { id: 4, parent_id: 2, name: "Leaf 1" },
  ], keys));
  const [openIds, setOpenIds] = useState<number[]>([1]);

  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setData,
    openIds,
    renderNode: ({ id, node, open }) => (
      <div>
        <button onClick={() => setOpenIds(prev =>
          open ? prev.filter(i => i !== id) : [...prev, id]
        )}>
          {open ? '▼' : '►'}
        </button>
        {node.name}
      </div>
    ),
  });

  return <div>{renderTree()}</div>;
}

This example introduces an openIds state to keep track of which nodes are expanded. We’ve added a toggle button to each node that updates this state, allowing users to expand and collapse branches of the tree.

Checkable Nodes

For scenarios where you need to select multiple nodes, he-tree-react supports checkable nodes with cascading selection:

import { useHeTree, sortFlatData, updateCheckedInFlatData } from "he-tree-react";
import { useState } from 'react';

export default function CheckableTree() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setData] = useState(() => sortFlatData([
    { id: 1, parent_id: null, name: "Root" },
    { id: 2, parent_id: 1, name: "Branch 1" },
    { id: 3, parent_id: 1, name: "Branch 2" },
    { id: 4, parent_id: 2, name: "Leaf 1" },
  ], keys));
  const [checkedIds, setCheckedIds] = useState<number[]>([]);

  const handleCheck = (id: number, checked: boolean) => {
    const [newCheckedIds] = updateCheckedInFlatData(data, checkedIds, id, checked, keys);
    setCheckedIds(newCheckedIds);
  };

  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setData,
    checkedIds,
    renderNode: ({ id, node, checked }) => (
      <div>
        <input
          type="checkbox"
          checked={checked || false}
          onChange={() => handleCheck(id, !checked)}
        />
        {node.name}
      </div>
    ),
  });

  return <div>{renderTree()}</div>;
}

This implementation adds checkboxes to each node and uses the updateCheckedInFlatData utility to handle cascading checks and unchecks throughout the tree structure.

Cultivating Performance: Virtual Lists

When dealing with large datasets, rendering every node can lead to performance issues. he-tree-react offers a virtual list feature to efficiently handle big data:

import { useHeTree, sortFlatData } from "he-tree-react";
import { useState } from 'react';

function generateLargeDataset() {
  // Generate a large dataset for demonstration
  const data = [];
  for (let i = 0; i < 10000; i++) {
    data.push({ id: i, parent_id: i > 0 ? Math.floor((i - 1) / 10) : null, name: `Node ${i}` });
  }
  return data;
}

export default function VirtualTree() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setData] = useState(() => sortFlatData(generateLargeDataset(), keys));

  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setData,
    virtual: true,
    renderNode: ({ node }) => <div>{node.name}</div>,
  });

  return <div style={{ height: '400px', overflow: 'auto' }}>
    {renderTree()}
  </div>;
}

By setting the virtual prop to true, he-tree-react will only render the nodes that are currently visible in the viewport, greatly improving performance for large trees.

Pruning and Grafting: Updating Tree Data

he-tree-react provides utilities for modifying tree data structures. Here’s an example of adding and removing nodes:

import { useHeTree, sortFlatData, addToFlatData, removeByIdInFlatData } from "he-tree-react";
import { useState } from 'react';

export default function EditableTree() {
  const keys = { idKey: 'id', parentIdKey: 'parent_id' };
  const [data, setData] = useState(() => sortFlatData([
    { id: 1, parent_id: null, name: "Root" },
    { id: 2, parent_id: 1, name: "Branch 1" },
    { id: 3, parent_id: 1, name: "Branch 2" },
  ], keys));

  const addNode = (parentId: number | null) => {
    const newNode = { id: Date.now(), parent_id: parentId, name: "New Node" };
    setData(prev => {
      const newData = [...prev];
      addToFlatData(newData, newNode, 0, keys);
      return newData;
    });
  };

  const removeNode = (id: number) => {
    setData(prev => {
      const newData = [...prev];
      removeByIdInFlatData(newData, id, keys);
      return newData;
    });
  };

  const { renderTree } = useHeTree({
    ...keys,
    data,
    dataType: 'flat',
    onChange: setData,
    renderNode: ({ id, node }) => (
      <div>
        {node.name}
        <button onClick={() => addNode(id)}>Add Child</button>
        <button onClick={() => removeNode(id)}>Remove</button>
      </div>
    ),
  });

  return <div>{renderTree()}</div>;
}

This example demonstrates how to use the addToFlatData and removeByIdInFlatData utilities to modify the tree structure dynamically.

Conclusion: Growing Your React Forest

he-tree-react provides a robust foundation for building complex tree structures in React applications. From basic rendering to advanced features like drag-and-drop, checkable nodes, and virtual lists, this library offers the tools you need to create intuitive and performant tree components.

By leveraging the power of he-tree-react, you can easily implement hierarchical data displays, file system explorers, organizational charts, and much more. The library’s flexibility allows you to customize the appearance and behavior of your trees to fit your specific project requirements.

As you continue to explore the capabilities of he-tree-react, you’ll discover even more ways to enhance your React applications with powerful tree components. Whether you’re building a small project or a large-scale application, he-tree-react is a valuable addition to your React toolkit, helping you create dynamic and interactive user interfaces with ease.

If you’re interested in exploring more React libraries and components, check out these related articles:

These articles cover related topics such as sortable trees, dynamic UIs, drag-and-drop functionality, transitions, and list virtualization, which can complement your knowledge of tree structures and enhance your React development skills.

Comments