Ant Design Tree组件支持点击节点展开收缩

Ant Design组件库中,Tree组件只支持点击父节点左侧的展开按钮进行展开/收缩操作,但是展开按钮很小,有时会不便于操作,这里展示了通过利用Tree组件的expandedKeysonExpand属性,将Tree组件的展开收缩改为受控行为来完成这一功能。

模拟数据

自定义data模拟数据

javascript 复制代码
// Tree组件模拟数据
const data = [
  {
    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-1',
        key: '0-0-1',
      },
    ],
  },
  {
    title: '0-2',
    key: '0-2',
  },
]

属性定义

定义Tree组件数据属性treeData及受控属性expandedKeys/onExpand

javascript 复制代码
const [treeData, setTreeData] = useState([])
const [expandedKeys, setExpandedKeys] = useState([])

const handleExpand = (expandedKeys) => {
  setExpandedKeys(expandedKeys)
}

数据生成

通过data数据递归生成树节点数据,并定义树节点的点击事件来控制树节点的展开/收缩

jsx 复制代码
// 递归生成树节点
const getTreeItem = useCallback(
  (item) => {
    const treeItem = {
      title: <div onClick={() => handleClick(item.title, treeItem.children)}>{item.title}</div>,
      key: item.title,
    }

    if (item.children) {
      treeItem.children = item.children.map((item) => getTreeItem(item))
    }

    return treeItem
  },
  [handleClick],
)

// 树节点点击事件
const handleClick = useCallback(
  (key, children) => {
    // 判断当前点击的节点key是否在expandedKeys中
    const index = expandedKeys.findIndex((item) => item === key)

    if (index === -1) {
      // 如果不在expandedKeys中并且该节点有子节点,则将当前节点key添加到expandedKeys中(展开)
      children?.length &&
        setExpandedKeys((keys) => {
          const newKeys = [...keys, key]
          return newKeys
        })
    } else {
      // 如果在expandedKeys中,则将当前节点key从expandedKeys中删除(收缩)
      setExpandedKeys((keys) => keys.filter((item) => item !== key))
    }
  },
  [expandedKeys],
)

useEffect(() => {
  // 初始化树节点(这里可以根据实际情况进行获取数据)
  const treeData = data.map((item) => getTreeItem(item))
  setTreeData(treeData)
}, [])

Tree组件使用

jsx 复制代码
<Tree
  showLine
  showIcon
  blockNode
  selectable={false}
  treeData={treeData}
  expandedKeys={expandedKeys}
  onExpand={handleExpand}
/>

动画优化

完成后发现可以实现点击节点进行展开/收缩功能,但是丢失了展开/收缩时的动画,下面是利用React Hook中的useDeferredValue来解决展开/收缩时的动画效果丢失问题。

jsx 复制代码
//通过useDeferredValue来获取expandKeys的延迟版本
const deferredExpandedKeys = useDeferredValue(expandedKeys)
  
//...
  
//在Tree组件中用deferredExpandedKeys来代替expandedKeys
<Tree
  showLine
  showIcon
  blockNode
  selectable={false}
  treeData={treeData}
  expandedKeys={deferredExpandedKeys}
  onExpand={handleExpand}
/>

完整代码

jsx 复制代码
import React, { useState, useCallback, useEffect, useDeferredValue } from 'react'
import { Tree } from 'antd'

// Tree组件模拟数据
const data = [
  {
    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-1',
        key: '0-0-1',
      },
    ],
  },
  {
    title: '0-2',
    key: '0-2',
  },
]

const App = () => {
  const [treeData, setTreeData] = useState([])
  const [expandedKeys, setExpandedKeys] = useState([])
  const deferredExpandedKeys = useDeferredValue(expandedKeys)

  const handleExpand = (expandedKeys) => {
    setExpandedKeys(expandedKeys)
  }

  // 树节点点击事件
  const handleClick = useCallback(
    (key, children) => {
      // 判断当前点击的节点key是否在expandedKeys中
      const index = expandedKeys.findIndex((item) => item === key)

      if (index === -1) {
        // 如果不在expandedKeys中并且该节点有子节点,则将当前节点key添加到expandedKeys中(展开)
        children?.length &&
          setExpandedKeys((keys) => {
            const newKeys = [...keys, key]
            return newKeys
          })
      } else {
        // 如果在expandedKeys中,则将当前节点key从expandedKeys中删除(收缩)
        setExpandedKeys((keys) => keys.filter((item) => item !== key))
      }
    },
    [expandedKeys],
  )

  // 递归生成树节点
  const getTreeItem = useCallback(
    (item) => {
      const treeItem = {
        title: <div onClick={() => handleClick(item.title, treeItem.children)}>{item.title}</div>,
        key: item.title,
      }

      if (item.children) {
        treeItem.children = item.children.map((item) => getTreeItem(item))
      }

      return treeItem
    },
    [handleClick],
  )

  useEffect(() => {
    // 初始化树节点(这里可以根据实际情况进行获取数据)
    const treeData = data.map((item) => getTreeItem(item))
    setTreeData(treeData)
  }, [])

  return (
    <Tree
      showLine
      showIcon
      blockNode
      selectable={false}
      treeData={treeData}
      expandedKeys={deferredExpandedKeys}
      onExpand={handleExpand}
    />
  )
}

export default App
相关推荐
学吧别真挂了25 天前
Vue 3三大UI组件库全解析:从安装到实战
element·ant design·vant
qq_530245191 个月前
React 18/19 使用Ant Design全局弹窗message
前端·react.js·ant design·react 18
Jackson_Mseven1 个月前
如何从0到1搭建基于antd的monorepo库——使用rollup进行打包、lerna进行版本管理和发布(六)
前端·react.js·ant design
Jackson_Mseven1 个月前
如何从0到1搭建基于antd的monorepo库——使用dumi进行文档展示(五)
前端·react.js·ant design
睡不着的可乐1 个月前
Ant Design Vue 表格复杂数据合并单元格
前端·vue.js·ant design
Jackson_Mseven1 个月前
如何从0到1搭建基于antd的monorepo库——实现JsonSchemaForm组件(三)
前端·react.js·ant design
反复的大魔王1 个月前
Ant Design Vue的日历组件(Calendar)在中文语言包环境下设置以周日开始?
vue.js·ant design
windyrain1 个月前
ant design pro 模版简化工具
前端·react.js·ant design
用户9290412768552 个月前
使用Echarts的tree图完成一个组织架构图
ant design
leafnote2 个月前
【antd】Switch,0和1,怎么办?
前端·ant design