You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React JS中基于UL/LI实现无外部依赖的可搜索树形视图(附JSON示例)

没问题!我帮你实现一个纯React的树形视图,自带搜索功能,完全用原生的ul/li标签,不需要任何外部依赖。下面是完整的实现方案:

纯React实现带搜索功能的树形视图(基于UL/LI,无外部库)

核心思路很简单:用递归组件渲染嵌套节点,通过状态管理搜索关键词和节点展开/折叠状态,同时实现智能过滤逻辑——只显示匹配的节点及其父节点。

1. 完整实现代码

import { useState, useMemo } from 'react';

// 你提供的示例JSON数据
const treeData = [
  { value: "General", label: "General", count: "12 / 30" },
  { value: "Accounting", label: "Accounting", count: "17 / 30" },
  { value: "Labour Management", label: "Labour Management", count: "05 / 30", 
    children: [
      { value: "Labour ManagementChild", label: "Labour ManagementChild", 
        children: [
          { value: "Labour ManagementSub Child", label: "Labour ManagementSub Child" }
        ]
      },
      { value: "Labour ManagementChild2", label: "Labour ManagementChild2" }
    ]
  }
];

const TreeView = () => {
  const [searchTerm, setSearchTerm] = useState('');
  const [expandedNodes, setExpandedNodes] = useState(new Set());

  // 切换节点展开/折叠状态
  const toggleNode = (nodeValue) => {
    setExpandedNodes(prev => {
      const newSet = new Set(prev);
      newSet.has(nodeValue) ? newSet.delete(nodeValue) : newSet.add(nodeValue);
      return newSet;
    });
  };

  // 智能过滤树数据:保留匹配节点及所有父节点
  const filteredTreeData = useMemo(() => {
    if (!searchTerm.trim()) return treeData;

    const filterNodes = (nodes) => {
      return nodes.reduce((acc, node) => {
        // 检查当前节点是否匹配搜索词
        const isMatch = node.label.toLowerCase().includes(searchTerm.toLowerCase());
        // 递归过滤子节点
        const filteredChildren = node.children ? filterNodes(node.children) : [];
        // 当前节点匹配 或 有匹配的子节点,就保留该节点
        if (isMatch || filteredChildren.length > 0) {
          acc.push({
            ...node,
            children: filteredChildren.length > 0 ? filteredChildren : undefined
          });
        }
        return acc;
      }, []);
    };

    return filterNodes(treeData);
  }, [searchTerm]);

  return (
    <div style={{ padding: '20px', maxWidth: '400px' }}>
      {/* 搜索输入框 */}
      <input
        type="text"
        placeholder="搜索节点..."
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        style={{ width: '100%', padding: '8px', marginBottom: '15px', borderRadius: '4px', border: '1px solid #ddd' }}
      />

      {/* 树形视图容器 */}
      <ul style={{ listStyle: 'none', paddingLeft: '0', margin: '0' }}>
        {filteredTreeData.map(node => (
          <TreeNode
            key={node.value}
            node={node}
            isExpanded={expandedNodes.has(node.value)}
            toggleNode={toggleNode}
          />
        ))}
      </ul>
    </div>
  );
};

// 递归渲染的TreeNode组件
const TreeNode = ({ node, isExpanded, toggleNode }) => {
  const hasChildren = node.children && node.children.length > 0;

  return (
    <li style={{ margin: '5px 0', paddingLeft: '15px', borderLeft: '1px solid #eee' }}>
      {/* 节点头部:展开按钮 + 标签 + 计数 */}
      <div 
        style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer', padding: '4px' }}
        onClick={() => hasChildren && toggleNode(node.value)}
      >
        {hasChildren && (
          <span style={{ fontSize: '12px' }}>
            {isExpanded ? '▼' : '▶'}
          </span>
        )}
        <span>{node.label}</span>
        {node.count && (
          <span style={{ marginLeft: 'auto', color: '#666', fontSize: '12px' }}>
            {node.count}
          </span>
        )}
      </div>

      {/* 渲染子节点(仅当展开且有子节点时) */}
      {isExpanded && hasChildren && (
        <ul style={{ listStyle: 'none', paddingLeft: '10px', marginTop: '4px' }}>
          {node.children.map(childNode => (
            <TreeNode
              key={childNode.value}
              node={childNode}
              isExpanded={false} // 子节点默认折叠,可按需改为记忆状态
              toggleNode={toggleNode}
            />
          ))}
        </ul>
      )}
    </li>
  );
};

export default TreeView;

2. 关键功能说明

搜索过滤逻辑

  • 用useMemo缓存过滤结果,避免不必要的重复计算
  • 递归遍历节点时,会自动保留匹配节点的所有父节点,确保用户能看到匹配节点的层级关系
  • 搜索不区分大小写,适配更友好的使用场景

展开/折叠功能

  • 用Set存储展开的节点value(保证唯一性),状态管理更高效
  • 只有包含子节点的节点才会显示展开/折叠按钮,点击头部即可切换状态

样式自定义

  • 用内联样式实现了基础的层级缩进和视觉区分,你可以轻松替换成自己的CSS类
  • 节点左侧的边框强化了层级感,展开/折叠按钮用简单符号代替图标,无需额外资源

3. 使用方法

直接把这个TreeView组件导入到你的React项目中即可使用,不需要安装任何外部依赖。你可以根据需求调整:

  • 搜索逻辑(比如支持搜索value或count)
  • 展开/折叠的默认状态
  • 整体样式风格

内容的提问来源于stack exchange,提问作者Nischal Tyagi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 14:57:57