Skip to main content

With Headless Tree, there are multiple ways how to manage the state of the tree. By default, Headless Tree manages all parts of the state itself, and you just need to provide the data. Alternatively, you can opt into managing the entirety of the tree state yourself, which is useful if you want to change the state from outside, or read from the state from outside. Finally, you can also manage individual parts of the state.

Note that the tree state only includes information that relates to how the tree is displayed, not parts that are specific to the data that composes the tree. For example, when you include the drag-and-drop feature, a dnd state sub-state is exposed that contains the IDs of dragged items, the item that the user is currently dragging over, and the position of where the item would be dropped. When you choose to manage that state yourself, you can read those pieces of information or control them yourself. However, the data of the tree, as well as its mutation followed by the drop event, is not part of the state. You need to handle these data mutations yourself outside of the tree state.

Another important part to keep in mind is that which parts of the state are exposed depends on the features that are included. The aforementioned dnd state property will be completely ignored if the dragAndDropFeature is not included in the tree configuration, as will be dnd-related update handlers.

Letting headless-tree manage the state

The simplest option is to let Headless Tree manage the entirety of the state. This is done by default, so you do not have to do anything. You can also supply a initialState prop in the tree configuration to define the initial state.

Managing individual feature states

If you are interested in specific parts of the state, you can opt into managing those parts of the state while still leaving the rest up to Headless Tree. To manage one part of the state, you need to provide the sub property of the state in the state config prop, as well as a state setter for that sub state directly to config. The state setter for a property stateProperty is always named setStateProperty.

For example, if you want to manage the set of selected items and the set of expanded items, you need to provide

useTree({
state: { selectedItems, expandedItems },
setSelectedItems,
setExpandedItems,
features: [syncDataLoaderFeature, selectionFeature],
// ...
});

Note that, while expanded items are part of the core functionality, item selections are part of the selection feature, and thus need the feature to be included in the tree configuration.

The type of the state depends on the specific state. For a state type T, the setter is of type

type SetStateFn<T> = (updaterOrValue: T | ((old: T) => T)) => void;

This is exactly the type of state setters that React uses for useState calls, so you can use a useState hook to let React manage the state property within your own code, and directly pass the specific state and its setter to Headless Tree.

const [selectedItems, setSelectedItems] = useState<string[]>([]);

useTree({
state: { selectedItems },
setSelectedItems,
// ...
});

Manage the entire state yourself

As another alternative, you can also choose to manage the entirety of the state yourself. Instead of passing individual sub properties through the state config prop, you can pass the entire state object as that property.

Similarly, you do not pass individual state setters named after each state property, but pass one setState method to the config that will be called whenever the state changes. The type of the state setter again is the same as the one used by React's useState hook.