트리

트리 (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 ×
rootStyle 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으로 커스터마이즈할 수 있습니다.

더 알아보기 (Learn more)