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
相关推荐
飞翔的渴望11 天前
antd3升级antd5总结
前端·react.js·ant design
袋鼠云数栈UED团队3 个月前
浅谈数栈产品里的 Descriptions 组件
前端·react.js·ant design
自白3 个月前
关于我是如何二次开发了 antd-vue 的a-range-picker组件,同时还添加了 vscod智能提示这件事
vue.js·visual studio code·ant design
袋鼠云数栈UED团队5 个月前
在 React 项目中 Editable Table 的实现
前端·react.js·ant design
孟宪磊mxl5 个月前
Element Plus& Ant Design(react) 表格的分页封装
vue.js·react.js·ant design·editplus
程序员也要学好英语5 个月前
搭建 react + antd 技术栈的测试框架
react.js·jest·ant design
Point5 个月前
[源码分析] Antd-RC-Notification
前端·源码阅读·ant design
Wxh161446 个月前
Ant Design 自定义组件空状态速记
前端·javascript·ant design
不喝酒不会写代码7 个月前
react使用antd-useWatch的坑(react太难了)
react.js·ant design
花笙_7 个月前
antd form表单赋值Switch不生效
前端·ant design