这是我参与「第五届青训营」伴学笔记创作活动的第 14 天,让正式我们开始 treeSelect 组建的编写。这篇文章首先先实现树形菜单的展开收起、选择相关的功能。
功能实现
展开收起树形结构
我们将使用一个Set来存储展开的节点,当点击展开/收起按钮时,我们将节点的value添加到Set中,如果已经存在,则删除。 这里使用react hooks的useState来实现。为的是在展开/收起节点时,重新渲染组件。
const [expanded, setExpanded] = useState(new Set());
然后再定义一个handleExpand函数,用来处理展开/收起节点的逻辑。
const handleExpand = (node: TreeNode<T>) => {
const newExpanded = new Set(expanded);
if (newExpanded.has(node.value)) {
newExpanded.delete(node.value);
} else {
newExpanded.add(node.value);
}
setExpanded(newExpanded);
};
在上述代码中,我们使用了一个新的Set来存储展开的节点,这是因为我们不希望直接修改原来的Set,而是返回一个新的Set。这样可以避免在修改Set时,引起不必要的错误。
渲染部分,我们需要判断节点是否有子节点,如果有子节点,我们就渲染展开/收起按钮。并且要判断当前节点是否已经展开,如果已经展开,我们就渲染收起按钮,否则渲染展开按钮。
{expandable ? (
node.children && node.children.length > 0 ? (
expanded.has(node.value) ? (
<IoMdArrowDropdown />
) : (
<IoMdArrowDropright />
)
) : (
<IoMdArrowDropdown className="space" />
)
) : null}
这里使用的图标是来自于react-icons库,用着简单方便。
选择节点
我们将使用一个数组来存储选中的节点,当点击选择框时,我们将节点添加到数组中,如果已经存在,则删除。同样是使用useState来实现。
const [selected, setSelected] = useState<TreeNode<T>[]>([]);
然后再定义一个handleSelect函数,用来处理选择节点的逻辑。
const handleSelect = (node: TreeNode<T>) => {
const newSelected = [...selected];
const index = newSelected.findIndex((item) => item.value === node.value);
if (index >= 0) {
newSelected.splice(index, 1);
} else {
newSelected.push(node);
}
setSelected(newSelected);
};
这里的逻辑和展开/收起节点的逻辑类似,在后续有更多的需求时,可以在这里进行扩展。
渲染部分,我们需要判断是否需要显示选择框,如果需要,我们就渲染选择框。并且要判断当前节点是否已经选中,如果已经选中,我们就渲染选中状态的选择框,否则渲染未选中状态的选择框。
{checkable ? (
<div
className={checkBoxClass}
onClick={(e) => {
e.stopPropagation();
handleSelect(node);
}}
data-testid={`checkBoxContainer` + node.label}
>
<CheckBox
displayIcon={checkable}
icon={icon}
isSelected={selected.includes(node)}
/>
</div>
) : null}
这里使用的CheckBox组件是我之前写的一个组件,用来实现复选框的功能,这里就不贴出来了。