Skip to main content

Frequently Asked Questions

The following are a list of questions and answers that I collected from the issue tracker of react-complex-tree.

Feel free to contribute additional questions and answers if you think they are relevant.

I implemented a tree using StaticTreeDataProvider, how can I change the tree state, like adding or updating items?

If you want to react to tree state changes, the easiest way to go forward is implementing a custom TreeDataProvider, though it is also possible with static tree data providers.

Here is some documentation on how to achieve this with static tree data providers, and here is some documentation on how to do it with custom tree data providers.

There you can implement logic that reacts to change events. If you need more control, you can also use a controlled environment rather than an uncontrolled environment, then you get complete control over your data.

Reference: #244

When I implement renderRenameInput, rename hooks are not called when clicking the rename submit button

Make sure to add ref={props.submitButtonRef} to the props of the submit button, then it should work. RCT checks if the input is blurred, i.e. the user clicks away from the input, and when that is the case, it cancels the renaming and hides the rename input. It uses a ref on the button to find out that the user is not actually cancelling the renaming, but clicking on the submit button. So without the ref, it thinks the user is just clicking away from the input, and unmounts the button and input before the submit logic can complete.

Reference: #247

How can I implement custom drop handlers? What is the difference between linearIndex and childIndex?

The gist is essentially just to implement the onDrop handler, you can use the implementation in the uncontrolled environment for that as reference, although it is a bit verbose and messy to be honest.

When the user is dropping an item, he is hovering over an ("target") item. The target type provides details where on that item that is:

  • "item": User is dropping in the center part of an item. This means the item is a folder and props.canDropOnFolder is true, or it is not a folder and props.canDropOnNonFolder is true. The expectation is that the dragged items are inserted into the hovered target item. The target variable provided to the onDrop handler is of type DraggingPositionItem
  • "between-items": User is either dragging on the top part or bottom part of the hovered item. The expactation is that the dragged items are inserted above the target item if they are hovering on the top part, or below if they are hovering on the bottom part. The target variable provided to the onDrop handler is of type DraggingPositionBetweenItems. The variable target.linePosition has the value "top" or "bottom" and determines this distinction.
  • "root": The user is dragging somewhere on the the top-level of the tree. This means, either the tree is empty, and the user is dropping on the empty tree container, or the container in which the tree items are rendered extends downwards of the last item, and the user is dropping there. The expecation is that the item is inserted in the top-level of the tree at the bottom. This is demonstrated in this demo, and was introduced as fix for #182 (commit). In implementation, I think you can handle this identical to the targetType "item", since in the case of "root", the other props are identical to the "item" case with the referencing item being the root item. The target variable provided to the onDrop handler is of type DraggingPositionRoot

Regarding linearIndex and childIndex: childIndex refers to the position of an item within its parent. linearIndex refers to the overall position of the item within the entire tree, would all items be laid out in a linear manner. As an example:

image

The item "Black" has the childIndex 2, and the linearIndex 5.

Reference: #211

When dropping items on files (i.e. non-folders) with canDropOnNonFolder enabled, the dropped items vanish.

This is by design. Dropping an item on a non-folder adds it to its children, but does not enable the isFolder flag for the drop target. Items are only rendered with children if they have the isFolder flag set to true.

  • If you want users to drop only on folders, enable canDropOnFolder instead of canDropOnNonFolder, and also canReorderItems if you want to allow reordering
  • If you want to have items turn into folders with children when you drop an item on it, use a class as data provider that implements TreeDataProvider.onChangeItemChildren to handle the new items by turning the item into a folder and notifying rct of changes with the listeners passed by TreeDataProvider.onDidChangeTreeData. Or implement that accordingly with StaticTreeDataProvider or an controlled environment by handling that yourself.
  • Similarly, if you want other special handling of items that are dropped on other non-folder items, but not turn the target into a folder, handle the drop event just as above, but for this case it is very much necessary for rct not to impose implicit changes like the drop target automatically turning into a folder.

Reference: #228

How do I alternate background color of rows?

Currently, there is no direct support from the library for that. There are some workarounds that might be feasible for you:

  • Use background-image: repeat-linear-gradient on the parent for a repeating pattern that matches the items height.
  • Customize the styling with the DOM API:
[...document.getElementsByClassName("rct-tree-item-button")]
.filter((_, i) => i % 2 === 0)
.forEach(n => n.style.backgroundColor = "red")

Reference: #217

Can I turn off multi-select?

There is no direct property that controls this.

You should be able to achieve that by implementing a custom interaction mode. You can read about those here, and see an example implementation here. You can probably just copy the code from the latter link, and remove all logic that is related to multi item selection.

Another alternative would be to use a controlled environment, where you manage changes to the view state, including selected items, yourself. There you could also customize how item selection is handled.

Reference: #151

How can I hide an folder arrow when all children are moved?

Whether arrow is shown or not depends only on isFolder (previously hasChildren) prop of the node. So you have to change that property for the node when all children are moved out.

Reference: #188 and #70

I want the user to be able to drop items on the bottom of the tree or into an empty tree.

This already works by default. Please note that the drag-over needs to be performed over the tree container component for it to register on empty trees, so the tree container component needs a minimum height for it to work. You can customize this by giving a minimum height to the component rendered with renderTreeContainer which receives the containerProps.

Reference: #182

I want to control the search input state or render the input somewhere else.

If you just want to control the search state from outside, you can already do that via the ref from the tree. Note that the search state lives within a tree, not a tree environment, so you need to grab the ref from the tree component, not the environment. You can set the current search value with treeRef.setSearch(), or close the search by invoking it with null.

If you would also like to not show the included search bar, you can update the renderSearchInput prop to () => null.

Alternatively you could also adjust renderSearchInput to render a portal that mounts the search input in a different location within your place, and customize how the search bar looks like from there.

Reference: #159

I want the search input to behave differently

If you want the search to behave in a completely different way than the built-in search, it is not that difficult to implement a custom search yourself.

Here is a sample for a custom search, with the implementation for that sample here.

Reference: #383

I want the search input to always be visible

There is no easy option for this, and a custom reimplementation of the search input might be a cleaner solution, see the FAQ point above, but this should be doable with some hacks nonetheless.

The search input is rendered whenever the search value is not null, which it is by default. You can set the search value to an empty string instead of null with treeRef.setSearch(""). Remove the keyboard bindings for "abortSearch". In your renderSearchInput implementation, overwrite the onBlur handler with something new after what ...inputProps provides to the input.

Reference: #383

I want to register a drag-start event or another event that is not available through RCTs API.

Yeah, there are no direct hooks exposed to register for drag start events. But there are two other ways to hook into these kinds of event handlers yourself:

  • Custom interaction managers: You can use a custom interaction manager to customize the event handlers that are mounted to the item component. You can extend existing modes, so you can fairly easily add only the hook you need on top of existing event handlers.
  • Custom render logic: This would be more effort and something I wouldn't recommend just for this, but you can also customize the render logic for tree items and mount the event there.

Reference: #156

How can I implement a custom data provider?

You can look into the implementation of StaticTreeDataProvider for a more elaborate example: https://github.com/lukasbach/react-complex-tree/blob/main/packages/core/src/uncontrolledEnvironment/StaticTreeDataProvider.ts

Reference: #149

I want to react to user actions, but not when reorder-actions are called

You can use onPrimaryAction for that.

There are no good docs on the exact logic right now, but the intent is that it calls when the user clicks on an item with probably no tree-navigation related intent. The exact logic you can see in the implementation, and is pretty much

  • when shift is not pressed
  • when control is not pressed
  • [when the item is not a folder, or canInvokePrimaryActionOnItemContainer is true]

You can test when it is triggered in this example.

If you want to further customize which kind of user interaction triggers what, you can use custom interaction modes for that.

Reference: #251

Render methods for tree items or others unnecessarily re-render

Yes, the render method for items are retriggered on tree state changes, since every item is provided with information about the tree state in their props, so tree items wouldn't be able to display data they otherwise can.

You can memoize the item render methods directly with only those props that you use, this will ensure that items are not rerendered unless they need to be: https://codesandbox.io/s/react-complex-tree-playground-memo-demo-37mngx

Reference: #291

I want to animate expanding and collapsing subtrees

The library doesn't provide that functionality out of the box, but you can implement it yourself with some custom logic. Here is a sample implementation, and this is the storybook where you can see how it looks like.

Reference: #290

I want to enable RTL (right-to-left) mode for my tree

The library doesn't make any assumptions about how you render your tree, and you can fairly easily implement RTL mode in custom renderers yourself. However, if you are using the built-in default renderers, you can also enable RTL mode by providing the renderers with an rtl flag. Add {...createDefaultRenderers(10, true)} to either the Tree or TreeEnvironment component to enable RTL mode.

See here for more details.

Reference: #369