react-vtree
This package provides a lightweight and flexible solution for rendering large tree structures. It is built on top of the react-window library.
Attention! This library is entirely rewritten to work with the react-window
. If you are looking for the tree view solution for the react-virtualized, take a look at react-virtualized-tree.
Installation
npm i react-window react-vtree
yarn add react-window react-vtree
Usage
FixedSizeTree
Example
You can also take a look at the very similar example at the Storybook:
import {FixedSizeTree as Tree} from 'react-vtree';
const tree = {
name: 'Root #1',
id: 'root-1',
children: [
{
children: [
{id: 'child-2', name: 'Child #2'},
{id: 'child-3', name: 'Child #3'},
],
id: 'child-1',
name: 'Child #1',
},
{
children: [{id: 'child-5', name: 'Child #5'}],
id: 'child-4',
name: 'Child #4',
},
],
};
function* treeWalker(refresh) {
const stack = [];
stack.push({
nestingLevel: 0,
node: tree,
});
while (stack.length !== 0) {
const {
node: {children = [], id, name},
nestingLevel,
} = stack.pop();
const isOpened = yield refresh
? {
id,
isLeaf: children.length === 0,
isOpenByDefault: true,
name,
nestingLevel,
}
: id;
if (children.length !== 0 && isOpened) {
for (let i = children.length - 1; i >= 0; i--) {
stack.push({
nestingLevel: nestingLevel + 1,
node: children[i],
});
}
}
}
}
const Node = ({data: {isLeaf, name}, isOpen, style, toggle}) => (
<div style={style}>
{!isLeaf && (
<button type="button" onClick={toggle}>
{isOpen ? '-' : '+'}
</button>
)}
<div>{name}</div>
</div>
);
const Example = () => (
<Tree treeWalker={treeWalker} itemSize={30} height={150} width={300}>
{Node}
</Tree>
);
Props
The component receives all the props of the FixedSizeList
component except for the itemCount
. Additional properties are the following:
children
The Node
component that is responsible for rendering each node. It receives all the properties Row
recieves except for the index
prop plus the following properties:
data: object
- a data object yielded by the treeWalker
function.isOpen: boolean
- a current openness status of the node.toggle(): function
- a function to change the openness state of the node. It receives no arguments and can be provided directly as an onClick
handler.treeData: any
- any data provided via the itemData
property of the FixedSizeTree
component.
rowComponent: component
This property receives a custom Row
component for the FixedSizeList
that will override the default one. It can be used for adding new functionality to an existing one by wrapping the default Row
into a custom component.
* treeWalker(refresh: boolean)
An iterator function that walks around the tree and yields each node one by one flattening them to an array that can be easily displayed by FixedSizeList
component.
The function receives refresh
parameter. If it is true
, the component requests the full node update and expects the complete data object yielded. If it is false
, the component awaits only the node id to update the order of displayed nodes.
The data object should contain the following required properties:
id
- a unique identifier of the node.isOpenByDefault
- a default openness state of the node.
You can add any other property you need. This object will be sent directly to the Node
component.
Yielding the object gets the current openness state of the node. Basing on it, you should decide if the node's children are going to be rendered.
Methods
The component provides all the methods FixedSizeList
provides with the following changes:
scrollToItem(id: string | symbol, align?: Align): void
The scrollToItem
method receives node id
instead of index
.
async recomputeTree(options): void
This method runs the treeWalker
function again and, basing on the received options, updates either nodes or their order.
It receives options object with the following parameters:
-
opennessState: Record<string, boolean>
- nodes whose IDs are specified as keys of this object will be opened or closed according to boolean values. If the value is true
, node will be opened; otherwise, it will be closed. This object can be used for changing nodes' openness programmatically without re-creating the treeWalker
generator.
NOTE: If you specify both useDefaultOpenness
and opennessState
, opennessState
will be overridden by useDefaultOpenness
results.
-
refreshNodes: boolean
- if this parameter is true
, treeWalker
will receive refresh
option, and the component will expect the data object yielded. If this parameter is either false
or not provided, the component will expect string id.
-
useDefaultOpenness: boolean
- if this parameter is true
, openness state of all nodes will be reset to isOpenByDefault
. Nodes updated during the tree walking will use the new isOpenByDefault
value.
Types
FixedSizeNodeData
- object the treeWalker
generator function yields for each new node. By default, it contains only id
and isOpenByDefault
fields, but you can add any number of additional fields; they will be sent directly to the Node component. To describe that data, you have to create a new type that extends the FixedSizeNodeData
type.FixedSizeNodeComponentProps<T extends FixedSizeNodeData>
- props that Node
component receives. They are described in the Props children section.FixedSizeTreeProps<T extends FixedSizeNodeData>
- props that FixedSizeTree
component receives. Described in the Props section.FixedSizeTreeState<T extends FixedSizeNodeData>
- state that FixedSizeTree
component has.
VariableSizeTree
Example
You can also take a look at the very similar example at the Storybook:
import {VariableSizeTree as Tree} from 'react-vtree';
const tree = {
name: 'Root #1',
id: 'root-1',
children: [
{
children: [
{id: 'child-2', name: 'Child #2'},
{id: 'child-3', name: 'Child #3'},
],
id: 'child-1',
name: 'Child #1',
},
{
children: [{id: 'child-5', name: 'Child #5'}],
id: 'child-4',
name: 'Child #4',
},
],
};
function* treeWalker(refresh) {
const stack = [];
stack.push({
nestingLevel: 0,
node: tree,
});
while (stack.length !== 0) {
const {
node: {children = [], id, name},
nestingLevel,
} = stack.pop();
const isOpened = yield refresh
? {
defaultHeight: 30,
id,
isLeaf: children.length === 0,
isOpenByDefault: true,
name,
nestingLevel,
}
: id;
if (children.length !== 0 && isOpened) {
for (let i = children.length - 1; i >= 0; i--) {
stack.push({
nestingLevel: nestingLevel + 1,
node: children[i],
});
}
}
}
}
const Node = ({data: {isLeaf, name}, height, isOpen, style, toggle}) => (
<div style={style}>
{!isLeaf && (
<button type="button" onClick={toggle}>
{isOpen ? '-' : '+'}
</button>
)}
<div>{name}</div>
</div>
);
const Example = () => (
<Tree treeWalker={treeWalker} height={150} width={300}>
{Node}
</Tree>
);
Props
The component receives all the props of the VariableSizeList
component except for the itemCount
and itemSize
. itemSize
is still available but not required, and should be used only if the default behavior is not enough. Additional properties are the following:
children
The Node
component. It is the same as the FixedSizeTree
's one but receives two additional properties:
height: number
- a current height of the node.resize(newHeight: number, shouldForceUpdate?: boolean): function
- a function to change the height of the node. It receives two parameters:
newHeight: number
- a new height of the node.shouldForceUpdate: boolean
- an optional argument that will be sent to the resetAfterIndex
method.
rowComponent: component
This property receives a custom Row
component for the VariableSizeList
that will override the default one. It can be used for adding new functionality to an existing one by wrapping the default Row
into a custom component.
* treeWalker(refresh: boolean)
An iterator function that walks over the tree. It behaves the same as FixedSizeTree
's treeWalker
, but there one additional required property for the data object:
defaultHeight: number
- the default height of the node.
Methods
The component provides all the methods VariableSizeList
provides with the following changes:
scrollToItem(id: string | symbol, align?: Align): void
The scrollToItem
method receives node id
instead of index
.
resetAfterId(id: string | symbol, shouldForceUpdate: boolean = false): void
This method replaces the resetAfterIndex
method of VariableSizeList
, but works exactly the same. It receives node id
as a first argument.
async recomputeTree(options): void
This method works exactly the same as the FixedSizeTree
's one, but receives one additional option:
useDefaultHeight: boolean
- if this parameter is true
, the height of all nodes will be reset to defaultHeight
. Nodes updated during the tree walking will use the new defaultHeight
value.
Types
All types in this section are the extended variants of FixedSizeTree
types.
VariableSizeNodeData
VariableSizeNodeComponentProps<T extends VariableSizeNodeData>
.VariableSizeTreeProps<T extends VariableSizeNodeData>
.VariableSizeTreeState<T extends VariableSizeNodeData>
.