Persisting the Focus on Data Reload

The TreeView enables you to keep the focus on the currently focused item while reloading the data.

Using Hierarchical Item Indices

By default, the TreeView keeps the focus through hierarchical item indices. The indices are zero-based and the first root item has a 0 (zero) index. If the first root item has children, the first child acquires a 0_0 index, the second child—a 0_1 index, and so on.

The following example demonstrates how the focused item is preserved upon constant re-rendering of the TreeView.

const tree = [{
    text: 'Furniture', expanded: true, items: [
        { text: 'Tables & Chairs' }, { text: 'Sofas' }, { text: 'Occasional Furniture' }]
}, {
    text: 'Decor', expanded: true, items: [
        { text: 'Bed Linen' }, { text: 'Curtains & Blinds' }, { text: 'Carpets' }]
}];

class App extends React.Component {
    state = { data: tree };

    render() {
        return <TreeView data={this.state.data} />;
    }

    componentDidMount() {
        setInterval(() => this.setState({ data: tree }), 2000);
    }
}

ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);

Using Item IDs

When you expect changes in the overall position of the TreeView items, set the focusIdField property to a field that uniquely describes the item—for example, an ID column from a database. The field id is not affected by position changes and the TreeView will be able to persist the focus on the current item.

The following example demonstrates how to add an item on every click before the clicked item and keep the focus on changing the item position. On each re-render, the TreeView executes a depth-first data search to resolve the focused item by its id because of possible position changes. For an additional optimization, set the getFocusHierarchicalIndex property to a custom function which will resolve a hierarchical item index by its id. For example, you can use cache which will be invalidated on each data reload.

const tree = [{
    text: 'Furniture', expanded: true, items: [
        { text: 'Tables & Chairs' }, { text: 'Sofas' }, { text: 'Occasional Furniture' }]
}, {
    text: 'Decor', expanded: true, items: [
        { text: 'Bed Linen' }, { text: 'Curtains & Blinds' }, { text: 'Carpets' }]
}];

class App extends React.Component {
    addedItemsCounter = 0;
    state = { data: tree };

    render() {
        return <TreeView focusIdField='text' data={this.state.data} onItemClick={this.onItemClick} />;
    }

    onItemClick = (event) => {
        const data = this.state.data.slice();

        if (data.includes(event.item)) {
            data.unshift({ text: `added item #${this.addedItemsCounter++}` });
        } else {
            const parentIndex = data[data.length - 1].items.includes(event.item) ?
                    data.length - 1 : data.length - 2;

            data[parentIndex] = Object.assign(
                {},
                data[parentIndex],
                { items: data[parentIndex].items.slice() }
            );
            data[parentIndex].items.unshift({ text: `added item #${this.addedItemsCounter++}` });
        }

        this.setState({ data });
    }
}

ReactDOM.render(
    <App />,
    document.querySelector('my-app')
);
 /