Updating Expanded Items

The TreeView provides specific approaches for working with its data and updating the expanded field.

The available options are:

Mutating the Items Directly

The following example demonstrates how to directly update the TreeView items.

Example
View Source
Change Theme:

Mutating the Wrapper Objects

The following example demonstrates how to update the items by wrapping them before they are passed to the TreeView. In this way, the TreeView regards the expanded field as part of the wrappers which makes the incoming data immutable.

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

    function wrapTreeViewItems(items) {
        return items.map((item) => {
            return { item, items: item.items && wrapTreeViewItems(item.items) }
        });
    }

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

        render() {
            return <TreeView data={this.state.data} textField='item.text'
                        expandIcons={true} onExpandChange={this.onExpandChange} />;
        }
        onExpandChange = (event) => {
            event.item.expanded = !event.item.expanded;
            this.forceUpdate();
        }
    }

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

Copying Data before Each Update

The TreeView allows you create a copy of the data before each item update and avoid the mutation of the data.

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

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

        render() {
            return <TreeView data={this.state.data} expandIcons={true} onExpandChange={this.onExpandChange} />;
        }
        onExpandChange = (event) => {
            const data = this.state.data.slice();

            const itemIndex = data.indexOf(event.item);
            data[itemIndex] = {...event.item};
            data[itemIndex].expanded = !data[itemIndex].expanded;

            this.setState({ data });
        }
    }

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

Using a Helper Function

The processTreeViewItems helper function updates the items in an immutable way, that is, works with a copy of the data. While using processTreeViewItems is similar to creating a copy of the data before each item update, the data field of the application state does not change. That is why, the helper function approach is useful when other components depend on the data field.

The following example demonstrates how to introduce an additional state field (expand) which holds the IDs of the expanded items and is passed to processTreeViewItems on each re-render.

    const tree = [{
        text: 'Item1',
        items: [{ text: 'Item1.1' }, { text: 'Item1.2', items: [{ text: 'Item1.2.1' }] }]
    }, {
        text: 'Item2', disabled: true,
        items: [{ text: 'Item2.1' }, { text: 'Item2.2' }, { text: 'Item2.3' }]
    }, {
        text: 'Item3'
    }];

    class App extends React.Component {
        state = { items: tree, expand: { ids: ['Item2'], idField: 'text' } };

        render() {
            return (
                <TreeView
                    data={processTreeViewItems(this.state.items, { expand: this.state.expand })}
                    expandIcons={true} onExpandChange={this.onExpandChange}
                />
            );
        }

        onExpandChange = (event) => {
            let ids = this.state.expand.ids.slice();
            const index = ids.indexOf(event.item.text);

            // Add or remove the item ID (i.e. the item text) depending on
            // whether the item is expanded or collapsed.
            index === -1 ? ids.push(event.item.text) : ids.splice(index, 1);

            this.setState({ expand: { ids, idField: 'text' } });
        }
    }

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