트리
트리 (Tree)
폴더, 조직도, 생물 분류처럼 계층 관계를 가진 데이터를 트리 구조로 보여주는 컴포넌트입니다. 펼치기·접기·선택을 지원해요.
출처: 문서
본문
언제 사용하나요 (When To Use)
거의 모든 것이 트리 구조로 표현될 수 있습니다. 폴더, 조직 계층, 생물 분류, 국가 등이 그 예입니다. Tree 컴포넌트는 이런 것들 사이의 계층 관계를 표현하는 방법입니다. Tree 안에서 treeNode를 펼치고, 접고, 선택할 수도 있습니다.
예제 (Examples)
기본 (Basic)
가장 기본적인 사용법으로, checkable, selectable, disabled, defaultExpandKeys 등을 어떻게 쓰는지 보여줍니다.
import React from 'react';
import { Tree } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
const treeData: TreeDataNode[] = [
{
title: 'parent 1',
key: '0-0',
children: [
{
title: 'parent 1-0',
key: '0-0-0',
disabled: true,
children: [
{
title: 'leaf',
key: '0-0-0-0',
disableCheckbox: true,
},
{
title: 'leaf',
key: '0-0-0-1',
},
],
},
{
title: 'parent 1-1',
key: '0-0-1',
children: [{ title: <span style={{ color: '#1677ff' }}>sss</span>, key: '0-0-1-0' }],
},
],
},
];
const App: React.FC = () => {
const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
console.log('selected', selectedKeys, info);
};
const onCheck: TreeProps['onCheck'] = (checkedKeys, info) => {
console.log('onCheck', checkedKeys, info);
};
return (
<Tree
checkable
defaultExpandedKeys={['0-0-0', '0-0-1']}
defaultSelectedKeys={['0-0-1']}
defaultCheckedKeys={['0-0-0', '0-0-1']}
onSelect={onSelect}
onCheck={onCheck}
treeData={treeData}
/>
);
};
export default App;
제어형 트리 (Controlled Tree)
제어(controlled) 모드는 부모 노드가 자식 노드의 상태를 더 지능적으로 반영하게 합니다.
import React, { useState } from 'react';
import { Tree } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
const treeData: TreeDataNode[] = [
{
title: '0-0',
key: '0-0',
children: [
{
title: '0-0-0',
key: '0-0-0',
children: [
{ title: '0-0-0-0', key: '0-0-0-0' },
{ title: '0-0-0-1', key: '0-0-0-1' },
{ title: '0-0-0-2', key: '0-0-0-2' },
],
},
{
title: '0-0-1',
key: '0-0-1',
children: [
{ title: '0-0-1-0', key: '0-0-1-0' },
{ title: '0-0-1-1', key: '0-0-1-1' },
{ title: '0-0-1-2', key: '0-0-1-2' },
],
},
{
title: '0-0-2',
key: '0-0-2',
},
],
},
{
title: '0-1',
key: '0-1',
children: [
{ title: '0-1-0-0', key: '0-1-0-0' },
{ title: '0-1-0-1', key: '0-1-0-1' },
{ title: '0-1-0-2', key: '0-1-0-2' },
],
},
{
title: '0-2',
key: '0-2',
},
];
const App: React.FC = () => {
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>(['0-0-0', '0-0-1']);
const [checkedKeys, setCheckedKeys] = useState<React.Key[]>(['0-0-0']);
const [selectedKeys, setSelectedKeys] = useState<React.Key[]>([]);
const [autoExpandParent, setAutoExpandParent] = useState<boolean>(true);
const onExpand: TreeProps['onExpand'] = (expandedKeysValue) => {
console.log('onExpand', expandedKeysValue);
// if not set autoExpandParent to false, if children expanded, parent can not collapse.
// or, you can remove all expanded children keys.
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck: TreeProps['onCheck'] = (checkedKeysValue) => {
console.log('onCheck', checkedKeysValue);
setCheckedKeys(checkedKeysValue as React.Key[]);
};
const onSelect: TreeProps['onSelect'] = (selectedKeysValue, info) => {
console.log('onSelect', info);
setSelectedKeys(selectedKeysValue);
};
return (
<Tree
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={treeData}
/>
);
};
export default App;
드래그 가능 (draggable)
treeNode를 드래그해 다른 treeNode 뒤로 삽입하거나 다른 부모 TreeNode 안으로 삽입합니다.
import React, { useState } from 'react';
import { Tree } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
const x = 3;
const y = 2;
const z = 1;
const defaultData: TreeDataNode[] = [];
const generateData = (_level: number, _preKey?: React.Key, _tns?: TreeDataNode[]) => {
const preKey = _preKey || '0';
const tns = _tns || defaultData;
const children: React.Key[] = [];
for (let i = 0; i < x; i++) {
const key = `${preKey}-${i}`;
tns.push({ title: key, key });
if (i < y) {
children.push(key);
}
}
if (_level < 0) {
return tns;
}
const level = _level - 1;
children.forEach((key, index) => {
tns[index].children = [];
generateData(level, key, tns[index].children);
});
};
generateData(z);
const App: React.FC = () => {
const [gData, setGData] = useState(defaultData);
const [expandedKeys] = useState(['0-0', '0-0-0', '0-0-0-0']);
const onDragEnter: TreeProps['onDragEnter'] = (info) => {
console.log(info);
// expandedKeys, set it when controlled is needed
// setExpandedKeys(info.expandedKeys)
};
const onDrop: TreeProps['onDrop'] = (info) => {
console.log(info);
const dropKey = info.node.key;
const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split('-');
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]); // the drop position relative to the drop node, inside 0, top -1, bottom 1
const loop = (
data: TreeDataNode[],
key: React.Key,
callback: (node: TreeDataNode, i: number, data: TreeDataNode[]) => void,
) => {
for (let i = 0; i < data.length; i++) {
if (data[i].key === key) {
return callback(data[i], i, data);
}
if (data[i].children) {
loop(data[i].children!, key, callback);
}
}
};
const data = [...gData];
// Find dragObject
let dragObj: TreeDataNode;
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
// Drop on the content
loop(data, dropKey, (item) => {
item.children = item.children || [];
// where to insert. New item was inserted to the start of the array in this example, but can be anywhere
item.children.unshift(dragObj);
});
} else {
let ar: TreeDataNode[] = [];
let i: number;
loop(data, dropKey, (_item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
// Drop on the top of the drop node
ar.splice(i!, 0, dragObj!);
} else {
// Drop on the bottom of the drop node
ar.splice(i! + 1, 0, dragObj!);
}
}
setGData(data);
};
return (
<Tree
className="draggable-tree"
defaultExpandedKeys={expandedKeys}
draggable
blockNode
onDragEnter={onDragEnter}
onDrop={onDrop}
treeData={gData}
/>
);
};
export default App;
데이터 비동기 로딩 (load data asynchronously)
treeNode를 클릭해 펼칠 때 데이터를 비동기로 로딩합니다.
import React, { useState } from 'react';
import { Tree } from 'antd';
interface DataNode {
title: string;
key: string;
isLeaf?: boolean;
children?: DataNode[];
}
const initTreeData: DataNode[] = [
{ title: 'Expand to load', key: '0' },
{ title: 'Expand to load', key: '1' },
{ title: 'Tree Node', key: '2', isLeaf: true },
];
// It's just a simple demo. You can use tree map to optimize update perf.
const updateTreeData = (list: DataNode[], key: React.Key, children: DataNode[]): DataNode[] =>
list.map((node) => {
if (node.key === key) {
return {
...node,
children,
};
}
if (node.children) {
return {
...node,
children: updateTreeData(node.children, key, children),
};
}
return node;
});
const App: React.FC = () => {
const [treeData, setTreeData] = useState(initTreeData);
const onLoadData = ({ key, children }: any) =>
new Promise<void>((resolve) => {
if (children) {
resolve();
return;
}
setTimeout(() => {
setTreeData((origin) =>
updateTreeData(origin, key, [
{ title: 'Child Node', key: `${key}-0` },
{ title: 'Child Node', key: `${key}-1` },
]),
);
resolve();
}, 1000);
});
return <Tree loadData={onLoadData} treeData={treeData} />;
};
export default App;
검색 가능 (Searchable)
검색 가능한 트리입니다.
import React, { useMemo, useState } from 'react';
import { Input, Tree } from 'antd';
import type { TreeDataNode } from 'antd';
const { Search } = Input;
const x = 3;
const y = 2;
const z = 1;
const defaultData: TreeDataNode[] = [];
const generateData = (_level: number, _preKey?: React.Key, _tns?: TreeDataNode[]) => {
const preKey = _preKey || '0';
const tns = _tns || defaultData;
const children: React.Key[] = [];
for (let i = 0; i < x; i++) {
const key = `${preKey}-${i}`;
tns.push({ title: key, key });
if (i < y) {
children.push(key);
}
}
if (_level < 0) {
return tns;
}
const level = _level - 1;
children.forEach((key, index) => {
tns[index].children = [];
generateData(level, key, tns[index].children);
});
};
generateData(z);
const dataList: { key: React.Key; title: string }[] = [];
const generateList = (data: TreeDataNode[]) => {
for (let i = 0; i < data.length; i++) {
const node = data[i];
const { key } = node;
dataList.push({ key, title: key as string });
if (node.children) {
generateList(node.children);
}
}
};
generateList(defaultData);
const getParentKey = (key: React.Key, tree: TreeDataNode[]): React.Key => {
let parentKey: React.Key;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey!;
};
const App: React.FC = () => {
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
const [searchValue, setSearchValue] = useState('');
const [autoExpandParent, setAutoExpandParent] = useState(true);
const onExpand = (newExpandedKeys: React.Key[]) => {
setExpandedKeys(newExpandedKeys);
setAutoExpandParent(false);
};
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
const newExpandedKeys = dataList
.map((item) => {
if (item.title.includes(value)) {
return getParentKey(item.key, defaultData);
}
return null;
})
.filter((item, i, self): item is React.Key => !!(item && self.indexOf(item) === i));
setExpandedKeys(newExpandedKeys);
setSearchValue(value);
setAutoExpandParent(true);
};
const treeData = useMemo(() => {
const loop = (data: TreeDataNode[]): TreeDataNode[] =>
data.map((item) => {
const strTitle = item.title as string;
const index = strTitle.indexOf(searchValue);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchValue.length);
const title =
index > -1 ? (
<span key={item.key}>
{beforeStr}
<span style={{ color: '#f50' }}>{searchValue}</span>
{afterStr}
</span>
) : (
<span key={item.key}>{strTitle}</span>
);
if (item.children) {
return { title, key: item.key, children: loop(item.children) };
}
return {
title,
key: item.key,
};
});
return loop(defaultData);
}, [searchValue]);
return (
<div>
<Search style={{ marginBottom: 8 }} placeholder="Search" onChange={onChange} />
<Tree
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
treeData={treeData}
/>
</div>
);
};
export default App;
라인이 있는 트리 (Tree with line)
노드 사이를 연결선으로 보여주는 트리입니다. showLine으로 켜고 switcherIcon으로 기본 아이콘을 커스터마이즈합니다.
import React, { useState } from 'react';
import { CarryOutOutlined, CheckOutlined, FormOutlined } from '@ant-design/icons';
import { Select, Switch, Tree } from 'antd';
import type { TreeDataNode } from 'antd';
const treeData: TreeDataNode[] = [
{
title: 'parent 1',
key: '0-0',
icon: <CarryOutOutlined />,
children: [
{
title: 'parent 1-0',
key: '0-0-0',
icon: <CarryOutOutlined />,
children: [
{ title: 'leaf', key: '0-0-0-0', icon: <CarryOutOutlined /> },
{
title: (
<>
<div>multiple line title</div>
<div>multiple line title</div>
</>
),
key: '0-0-0-1',
icon: <CarryOutOutlined />,
},
{ title: 'leaf', key: '0-0-0-2', icon: <CarryOutOutlined /> },
],
},
{
title: 'parent 1-1',
key: '0-0-1',
icon: <CarryOutOutlined />,
children: [{ title: 'leaf', key: '0-0-1-0', icon: <CarryOutOutlined /> }],
},
{
title: 'parent 1-2',
key: '0-0-2',
icon: <CarryOutOutlined />,
children: [
{ title: 'leaf', key: '0-0-2-0', icon: <CarryOutOutlined /> },
{
title: 'leaf',
key: '0-0-2-1',
icon: <CarryOutOutlined />,
switcherIcon: <FormOutlined />,
},
],
},
],
},
{
title: 'parent 2',
key: '0-1',
icon: <CarryOutOutlined />,
children: [
{
title: 'parent 2-0',
key: '0-1-0',
icon: <CarryOutOutlined />,
children: [
{ title: 'leaf', key: '0-1-0-0', icon: <CarryOutOutlined /> },
{ title: 'leaf', key: '0-1-0-1', icon: <CarryOutOutlined /> },
],
},
],
},
];
const App: React.FC = () => {
const [showLine, setShowLine] = useState<boolean>(true);
const [showIcon, setShowIcon] = useState<boolean>(false);
const [showLeafIcon, setShowLeafIcon] = useState<React.ReactNode>(true);
const onSelect = (selectedKeys: React.Key[], info: any) => {
console.log('selected', selectedKeys, info);
};
const handleLeafIconChange = (value: 'true' | 'false' | 'custom') => {
if (value === 'custom') {
return setShowLeafIcon(<CheckOutlined />);
}
if (value === 'true') {
return setShowLeafIcon(true);
}
return setShowLeafIcon(false);
};
return (
<div>
<div style={{ marginBottom: 16 }}>
showLine:{' '}
<Switch aria-label="Show connecting lines" checked={!!showLine} onChange={setShowLine} />
<br />
<br />
showIcon: <Switch aria-label="Show node icons" checked={showIcon} onChange={setShowIcon} />
<br />
<br />
showLeafIcon:{' '}
<Select
aria-label="Leaf icon"
defaultValue="true"
onChange={handleLeafIconChange}
options={[
{ label: 'True', value: 'true' },
{ label: 'False', value: 'false' },
{ label: 'Custom icon', value: 'custom' },
]}
/>
</div>
<Tree
showLine={showLine ? { showLeafIcon } : false}
showIcon={showIcon}
defaultExpandedKeys={['0-0-0']}
onSelect={onSelect}
treeData={treeData}
/>
</div>
);
};
export default App;
아이콘 커스터마이즈 (Customize Icon)
서로 다른 노드에 맞게 아이콘을 커스터마이즈할 수 있습니다.
import React from 'react';
import {
DownOutlined,
FrownFilled,
FrownOutlined,
MehOutlined,
SmileOutlined,
} from '@ant-design/icons';
import { Tree } from 'antd';
import type { TreeDataNode } from 'antd';
const treeData: TreeDataNode[] = [
{
title: 'parent 1',
key: '0-0',
icon: <SmileOutlined />,
children: [
{
title: 'leaf',
key: '0-0-0',
icon: <MehOutlined />,
},
{
title: 'leaf',
key: '0-0-1',
icon: ({ selected }) => (selected ? <FrownFilled /> : <FrownOutlined />),
},
],
},
];
const App: React.FC = () => (
<Tree
showIcon
defaultExpandAll
defaultSelectedKeys={['0-0-0']}
switcherIcon={<DownOutlined />}
treeData={treeData}
/>
);
export default App;
디렉토리 (directory)
내장 디렉토리 트리입니다. multiple은 ctrl(Windows) / command(Mac) 선택을 지원합니다.
import React from 'react';
import { Tree } from 'antd';
import type { GetProps, TreeDataNode } from 'antd';
type DirectoryTreeProps = GetProps<typeof Tree.DirectoryTree>;
const { DirectoryTree } = Tree;
const treeData: TreeDataNode[] = [
{
title: 'parent 0',
key: '0-0',
children: [
{ title: 'leaf 0-0', key: '0-0-0', isLeaf: true },
{ title: 'leaf 0-1', key: '0-0-1', isLeaf: true },
],
},
{
title: 'parent 1',
key: '0-1',
children: [
{ title: 'leaf 1-0', key: '0-1-0', isLeaf: true },
{ title: 'leaf 1-1', key: '0-1-1', isLeaf: true },
],
},
];
const App: React.FC = () => {
const onSelect: DirectoryTreeProps['onSelect'] = (keys, info) => {
console.log('Trigger Select', keys, info);
};
const onExpand: DirectoryTreeProps['onExpand'] = (keys, info) => {
console.log('Trigger Expand', keys, info);
};
return (
<DirectoryTree
multiple
draggable
defaultExpandAll
onSelect={onSelect}
onExpand={onExpand}
treeData={treeData}
/>
);
};
export default App;
펼침/접힘 아이콘 커스터마이즈 (Customize collapse/expand icon)
트리 노드의 펼침/접힘 아이콘을 커스터마이즈합니다.
import React from 'react';
import { DownOutlined } from '@ant-design/icons';
import { Tree } from 'antd';
import type { TreeDataNode, TreeProps } from 'antd';
const treeData: TreeDataNode[] = [
{
title: 'parent 1',
key: '0-0',
children: [
{
title: 'parent 1-0',
key: '0-0-0',
children: [
{
title: 'leaf',
key: '0-0-0-0',
},
{
title: 'leaf',
key: '0-0-0-1',
},
{
title: 'leaf',
key: '0-0-0-2',
},
],
},
{
title: 'parent 1-1',
key: '0-0-1',
children: [
{
title: 'leaf',
key: '0-0-1-0',
},
],
},
{
title: 'parent 1-2',
key: '0-0-2',
children: [
{
title: 'leaf',
key: '0-0-2-0',
},
{
title: 'leaf',
key: '0-0-2-1',
},
],
},
],
},
];
const App: React.FC = () => {
const onSelect: TreeProps['onSelect'] = (selectedKeys, info) => {
console.log('selected', selectedKeys, info);
};
return (
<Tree
showLine
switcherIcon={({ expanded }) => (
<DownOutlined
style={{ transform: `rotate(${expanded ? 0 : -90}deg)`, transition: 'transform 0.3s' }}
/>
)}
defaultExpandedKeys={['0-0-0']}
onSelect={onSelect}
treeData={treeData}
/>
);
};
export default App;
가상 스크롤 (Virtual scroll)
height prop으로 가상 리스트를 사용합니다.
import React from 'react';
import { Tooltip, Tree } from 'antd';
import type { TreeDataNode } from 'antd';
const dig = (path = '0', level = 3) => {
const list: TreeDataNode[] = [];
for (let i = 0; i < 10; i += 1) {
const key = `${path}-${i}`;
const treeNode: TreeDataNode = {
title: key,
key,
};
if (level > 0) {
treeNode.children = dig(key, level - 1);
}
list.push(treeNode);
}
return list;
};
const treeData = dig();
const MemoTooltip = React.memo(Tooltip);
const App: React.FC = () => (
<Tree
treeData={treeData}
height={233}
defaultExpandAll
titleRender={(item) => {
const title = item.title as React.ReactNode;
return <MemoTooltip title={title}>{title}</MemoTooltip>;
}}
/>
);
export default App;
중첩 노드로 스크롤 (Scroll to nested node)
제어 모드에서 Tree.useTree를 사용해 노드 경로를 얻고, 대상 노드로 스크롤하기 전에 expandedKeys를 업데이트합니다.
import React, { useRef, useState } from 'react';
import { Button, Flex, Tree } from 'antd';
import type { GetRef, TreeDataNode } from 'antd';
const TARGET_KEY = '0-1-1-1-1';
const createTree = (key = '0', level = 1): TreeDataNode => ({
key,
title: key,
children: level < 5 ? [0, 1].map((index) => createTree(`${key}-${index}`, level + 1)) : undefined,
});
const treeData = [createTree()];
const App: React.FC = () => {
const treeRef = useRef<GetRef<typeof Tree>>(null);
const [expandedKeys, setExpandedKeys] = useState<React.Key[]>([]);
const { getPath } = Tree.useTree(treeData, {});
const scrollTo = () => {
setExpandedKeys(getPath(TARGET_KEY).map(({ key }) => key));
treeRef.current?.scrollTo({ key: TARGET_KEY, align: 'top' });
};
return (
<Flex vertical gap="small">
<Button onClick={scrollTo}>scrollTo: {TARGET_KEY}</Button>
<Tree
ref={treeRef}
height={200}
treeData={treeData}
expandedKeys={expandedKeys}
onExpand={setExpandedKeys}
/>
</Flex>
);
};
export default App;
블록 노드 (Block Node)
import React from 'react';
import { Tree } from 'antd';
import type { TreeDataNode } from 'antd';
const treeData: TreeDataNode[] = [
{
title: 'parent',
key: '0',
children: [
{
title: 'child 1',
key: '0-0',
disabled: true,
},
{
title: 'child 2',
key: '0-1',
disableCheckbox: true,
},
],
},
];
const App: React.FC = () => (
<Tree checkable defaultSelectedKeys={['0-1']} defaultExpandAll treeData={treeData} blockNode />
);
export default App;
커스텀 시맨틱 DOM 스타일링 (Custom semantic dom styling)
classNames와 styles에 객체 또는 함수를 넘겨서 Tree의 시맨틱 DOM 스타일을 커스터마이즈할 수 있습니다.
import React from 'react';
import { Flex, Tree } from 'antd';
import type { GetProp, TreeProps } from 'antd';
import { createStaticStyles } from 'antd-style';
const classNames = createStaticStyles(({ css }) => ({
root: css`
padding: 8px;
border-radius: 4px;
`,
item: css`
border-radius: 2px;
`,
itemTitle: css`
font-size: 14px;
`,
}));
const treeData: TreeProps['treeData'] = [
{
title: 'parent 1',
key: '0-0',
children: [
{
title: 'parent 1-0',
key: '0-0-0',
children: [
{
title: 'leaf',
key: '0-0-0-0',
},
{
title: 'leaf',
key: '0-0-0-1',
},
],
},
{
title: 'parent 1-1',
key: '0-0-1',
children: [
{
title: 'leaf',
key: '0-0-1-0',
},
],
},
],
},
];
const styles: TreeProps['styles'] = {
root: { border: '2px solid #d9d9d9' },
item: { margin: '2px 0' },
};
const stylesFn: TreeProps['styles'] = (info): GetProp<TreeProps, 'styles', 'Return'> => {
if (!info.props.checkable) {
return {
root: {
border: `2px solid #E5D9F2`,
borderRadius: 4,
},
};
}
return {};
};
const App: React.FC = () => {
const sharedProps: TreeProps = {
treeData,
classNames,
autoExpandParent: true,
checkable: true,
};
return (
<Flex vertical gap="medium">
<Tree {...sharedProps} treeData={treeData} styles={styles} />
<Tree
{...sharedProps}
checkable={false}
treeData={treeData}
styles={stylesFn}
defaultExpandedKeys={['0-0-0', '0-0-1']}
defaultSelectedKeys={['0-0-1']}
defaultCheckedKeys={['0-0-0', '0-0-1']}
/>
</Flex>
);
};
export default App;
API
Common props ref:Common props
Tree props
| Property | Description | Type | Default | Version | Global Config |
|---|---|---|---|---|---|
| allowDrop | Whether to allow dropping on the node | ({ dropNode, dropPosition }) => boolean | - | × | |
| autoExpandParent | Whether to automatically expand a parent treeNode | boolean | false | × | |
| blockNode | Whether treeNode fill remaining horizontal space | boolean | false | × | |
| checkable | Add a Checkbox before the treeNodes | boolean | false | × | |
| checkedKeys | (Controlled) Specifies the keys of the checked treeNodes (PS: When this specifies the key of a treeNode which is also a parent treeNode, all the children treeNodes of will be checked; and vice versa, when it specifies the key of a treeNode which is a child treeNode, its parent treeNode will also be checked. When checkable and checkStrictly is true, its object has checked and halfChecked property. Regardless of whether the child or parent treeNode is checked, they won't impact each other |
string[] | {checked: string[], halfChecked: string[]} | [] | × | |
| checkStrictly | Check treeNode precisely; parent treeNode and children treeNodes are not associated | boolean | false | × | |
| classNames | Customize class for each semantic structure inside the component. Supports object or function. | Record<SemanticDOM, string> | (info: { props })=> Record<SemanticDOM, string> | - | 6.0.0 | |
| defaultCheckedKeys | Specifies the keys of the default checked treeNodes | string[] | [] | × | |
| defaultExpandAll | Whether to expand all treeNodes by default | boolean | false | × | |
| defaultExpandedKeys | Specify the keys of the default expanded treeNodes | string[] | [] | × | |
| defaultExpandParent | If auto expand parent treeNodes when init | boolean | true | × | |
| defaultSelectedKeys | Specifies the keys of the default selected treeNodes | string[] | [] | × | |
| disabled | Whether the tree is disabled | boolean | false | × | |
| draggable | Specifies whether this Tree or the node is draggable. Use icon: false to disable drag handler icon |
boolean | ((node: DataNode) => boolean) | { icon?: React.ReactNode | false, nodeDraggable?: (node: DataNode) => boolean } | false | config: 4.17.0 |
× |
| expandedKeys | (Controlled) Specifies the keys of the expanded treeNodes | string[] | [] | × | |
| fieldNames | Customize node title, key, children field name | object | { title: title, key: key, children: children } |
4.17.0 | × |
| filterTreeNode | Defines a function to filter (highlight) treeNodes. When the function returns true, the corresponding treeNode will be highlighted |
function(node) | - | × | |
| height | Config virtual scroll height. Will not support horizontal scroll when enabled | number | - | × | |
| icon | Insert a custom icon before the title. Need to set showIcon to true |
ReactNode | (props) => ReactNode | - | × | |
| loadData | Load data asynchronously | function(node) | - | × | |
| loadedKeys | (Controlled) Set loaded tree nodes. Need to work with loadData |
string[] | [] | × | |
| motion | Custom motion config for the tree | CSSMotionProps | - | × | |
| multiple | Allows selecting multiple treeNodes | boolean | false | × | |
Style on the root element, please use styles.root instead |
CSSProperties | - | 4.20.0 | × | |
| selectable | Whether it can be selected | boolean | true | × | |
| selectedKeys | (Controlled) Specifies the keys of the selected treeNodes, multiple selection needs to set multiple to true |
string[] | - | × | |
| showIcon | Controls whether to display the icon node (no default style) |
boolean | false | × | |
| showLine | Shows a connecting line | boolean | {showLeafIcon: ReactNode | ((props: AntTreeNodeProps) => ReactNode)} | false | × | |
| styles | Customize inline style for each semantic structure inside the component. Supports object or function. | Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> | - | 6.0.0 | |
| switcherIcon | Customize expand/collapse icons for tree nodes (Will not rotate automatically in showLine mode) |
ReactNode | ((props: AntTreeNodeProps) => ReactNode) | - | renderProps: 4.20.0 | × |
| switcherLoadingIcon | Customize loading icons for tree nodes | ReactNode | - | 5.20.0 | × |
| titleRender | Customize tree node title render | (nodeData) => ReactNode | - | 4.5.0 | × |
| treeData | The treeNodes data Array, if set it then you need not to construct children TreeNode. (key should be unique across the whole array) | array<{ key, title, children, [disabled, selectable] }> | - | × | |
| virtual | Disable virtual scroll when set to false | boolean | true | 4.1.0 | × |
| onCheck | Callback function for when the onCheck event occurs | function(checkedKeys, e:{checked: boolean, checkedNodes, node, event, halfCheckedKeys}) | - | × | |
| onDoubleClick | Callback function for when the user double clicks a treeNode | function(event, node) | - | × | |
| onDragEnd | Callback function for when the onDragEnd event occurs | function({event, node}) | - | × | |
| onDragEnter | Callback function for when the onDragEnter event occurs | function({event, node, expandedKeys}) | - | × | |
| onDragLeave | Callback function for when the onDragLeave event occurs | function({event, node}) | - | × | |
| onDragOver | Callback function for when the onDragOver event occurs | function({event, node}) | - | × | |
| onDragStart | Callback function for when the onDragStart event occurs | function({event, node}) | - | × | |
| onDrop | Callback function for when the onDrop event occurs | function({event, node, dragNode, dragNodesKeys}) | - | × | |
| onExpand | Callback function for when a treeNode is expanded or collapsed | function(expandedKeys, {expanded: boolean, node}) | - | × | |
| onLoad | Callback function for when a treeNode is loaded | function(loadedKeys, {event, node}) | - | × | |
| onRightClick | Callback function for when the user right clicks a treeNode | function({event, node}) | - | × | |
| onSelect | Callback function for when the user clicks a treeNode | function(selectedKeys, e:{selected: boolean, selectedNodes, node, event}) | - | × |
TreeNode props
| Property | Description | Type | Default | |
|---|---|---|---|---|
| checkable | When Tree is checkable, set TreeNode display Checkbox or not | boolean | - | |
| disableCheckbox | Disables the checkbox of the treeNode | boolean | false | |
| disabled | Disables the treeNode | boolean | false | |
| icon | Customize icon. When you pass component, whose render will receive full TreeNode props as component props | ReactNode | (props) => ReactNode | - | |
| isLeaf | Determines if this is a leaf node (effective when loadData is specified). false will force the TreeNode to be treated as a parent node |
boolean | - | |
| key | Used with (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys. P.S.: It must be unique in all of treeNodes of the tree | string | (internal calculated position of treeNode) | |
| selectable | Set whether the treeNode can be selected | boolean | true | |
| title | Title | ReactNode | --- |
DirectoryTree props
| Property | Description | Type | Default |
|---|---|---|---|
| expandAction | Directory opening logic, options: false | click | doubleClick |
string | boolean | click |
참고 (Note)
3.4.0 이전: treeNode의 수가 매우 많을 수 있지만, checkable=true일 때 계산 시간이 늘어납니다. 그래서 (예: this.treeNodesStates) 일부 계산을 캐시해 이중 계산을 피합니다. 하지만 이는 몇 가지 제한을 가져옵니다. treeNode를 비동기로 로딩할 때 트리를 다음과 같이 렌더링해야 합니다:
{
this.state.treeData.length ? (
<Tree>
{this.state.treeData.map((data) => (
<TreeNode />
))}
</Tree>
) : (
'loading tree'
);
}
Tree 메서드 (Tree Methods)
| Name | Description |
|---|---|
| scrollTo({ key: string | number; align?: 'top' | 'bottom' | 'auto'; offset?: number; autoExpand?: boolean }) | Scroll to key item in virtual scroll. autoExpand expands the target node in uncontrolled mode |
Tree Hooks
Tree.useTree
type Tree.useTree = (treeData: DataNode[], config: { fieldNames?: FieldNames }) => TreeInstance
Tree 데이터 유틸리티를 제공합니다. getPath(key)는 루트에서 대상 노드까지의 노드 엔티티를 반환하며, 제어 모드에서 expandedKeys를 업데이트하는 데 사용할 수 있습니다.
getPath는 안정적인 함수 정체성을 가지며 호출할 때 최신 treeData를 읽습니다. getPath에서 파생된 값을 메모이즈한다면 의존성 추적이 getPath 내부에서 캡처된 데이터를 관찰할 수 없으므로, 의존성 목록에 treeData와 조회 key를 포함하세요.
const { getPath } = Tree.useTree(treeData, {});
const path = useMemo(() => getPath(selectedKey), [getPath, selectedKey, treeData]);
시맨틱 DOM (Semantic DOM)
https://ant.design/components/tree/semantic.md
디자인 토큰 (Design Token)
컴포넌트 토큰 (Component Token - Tree)
| Token Name | Description | Type | Default Value |
|---|---|---|---|
| directoryNodeSelectedBg | Background color of selected directory node | string | #1677ff |
| directoryNodeSelectedColor | Text color of selected directory node | string | #fff |
| indentSize | Indent width of tree | number | 24 |
| nodeHoverBg | Background color of hovered node | string | rgba(0,0,0,0.04) |
| nodeHoverColor | Text color of hovered node | string | rgba(0,0,0,0.88) |
| nodeSelectedBg | Background color of selected node | string | #e6f4ff |
| nodeSelectedColor | Text color of selected node | string | rgba(0,0,0,0.88) |
| switcherSize | Switcher width of tree | number | 24 |
| titleHeight | Node title height | number | 24 |
글로벌 토큰 (Global Token)
| Token Name | Description | Type | Default Value |
|---|---|---|---|
| borderRadius | Border radius of base components | number | |
| borderRadiusSM | SM size border radius, used in small size components, such as Button, Input, Select and other input components in small size | number | |
| colorBgContainer | Container background color, e.g: default button, input box, etc. Be sure not to confuse this with colorBgElevated. |
string | |
| colorBgContainerDisabled | Control the background color of container in disabled state. | string | |
| colorBgTextHover | Control the background color of text in hover state. | string | |
| colorBorder | Default border color, used to separate different elements, such as: form separator, card separator, etc. | string | |
| colorFillQuaternary | The weakest level of fill color is suitable for color blocks that are not easy to attract attention, such as zebra stripes, color blocks that distinguish boundaries, etc. | string | |
| colorPrimary | Brand color is one of the most direct visual elements to reflect the characteristics and communication of the product. After you have selected the brand color, we will automatically generate a complete color palette and assign it effective design semantics. | string | |
| colorPrimaryBorder | The stroke color under the main color gradient, used on the stroke of components such as Slider. | string | |
| colorPrimaryHover | Hover state under the main color gradient. | string | |
| colorSplit | Used as the color of separator, this color is the same as colorBorderSecondary but with transparency. | string | |
| colorText | Default text color which comply with W3C standards, and this color is also the darkest neutral color. | string | |
| colorTextDisabled | Control the color of text in disabled state. | string | |
| colorTextQuaternary | The fourth level of text color is the lightest text color, such as form input prompt text, disabled color text, etc. | string | |
| colorWhite | Pure white color don't changed by theme | string | |
| controlInteractiveSize | Control the interactive size of control component. | number | |
| controlItemBgActiveDisabled | Control the background color of control component item when active and disabled. | string | |
| controlItemBgHover | Control the background color of control component item when hovering. | string | |
| fontFamily | The font family of Ant Design prioritizes the default interface font of the system, and provides a set of alternative font libraries that are suitable for screen display to maintain the readability and readability of the font under different platforms and browsers, reflecting the friendly, stable and professional characteristics. | string | |
| fontSize | The most widely used font size in the design system, from which the text gradient will be derived. | number | |
| fontSizeLG | Large font size | number | |
| fontWeightStrong | Control the font weight of heading components (such as h1, h2, h3) or selected item. | number | |
| lineHeight | Line height of text. | number | |
| lineType | Border style of base components | string | |
| lineWidth | Border width of base components | number | |
| lineWidthBold | The default line width of the outline class components, such as Button, Input, Select, etc. | number | |
| lineWidthFocus | Control the width of the line when the component is in focus state. | number | |
| marginXS | Control the margin of an element, with a small size. | number | |
| motionDurationFast | Motion speed, fast speed. Used for small element animation interaction. | string | |
| motionDurationMid | Motion speed, medium speed. Used for medium element animation interaction. | string | |
| motionDurationSlow | Motion speed, slow speed. Used for large element animation interaction. | string | |
| motionEaseInBack | Preset motion curve. | string | |
| motionEaseInOut | Preset motion curve. | string | |
| motionEaseOutBack | Preset motion curve. | string | |
| paddingXS | Control the extra small padding of the element. | number |
FAQ
ajax 데이터에서 defaultExpandAll이 동작하지 않는 이유는 무엇인가요? {#faq-default-expand-all}
default 접두사 props는 초기화할 때만 동작합니다. 그래서 defaultExpandAll은 ajax가 데이터를 로딩할 때 이미 실행된 상태입니다. expandedKeys를 제어하거나, 데이터가 로딩된 뒤 Tree를 렌더링해 모든 노드를 펼칠 수 있습니다.
가상 스크롤 제한 {#faq-virtual-scroll-limitation}
가상 스크롤은 보이는 영역의 항목만 렌더링합니다. 따라서 자동 너비(가로 스크롤이 필요한 긴 title 같은)를 지원하지 않습니다.
트리에서 disabled 노드의 동작 로직은 무엇인가요? {#faq-disabled-node}
Tree는 전도(conduction) 방식으로 데이터를 변경합니다. 체크나 자동 확장을 포함해, 현재 노드가 disabled가 될 때까지 부모/자식 노드로 상태를 전도합니다. 그래서 제어되는 노드가 disabled라면 자기 상태만 수정하고 다른 노드에는 영향을 주지 않습니다. 예를 들어 부모 노드에 자식 3개가 있고 그중 하나가 disabled라면, 부모 노드를 체크할 때 나머지 자식 2개만 체크됩니다. 마찬가지로 이 2개 자식 노드를 체크하면, disabled 노드의 체크 상태와 무관하게 부모가 체크됩니다.
이 전도 로직은 자식 노드를 체크해 disabled 부모의 체크 상태를 수정하는 것을 방지하고, 사용자가 클릭으로 직접 수정할 수 없게 하여 상호작용 충돌을 피합니다. 이 전도 로직을 수정하려면 checkStrictly prop으로 커스터마이즈할 수 있습니다.