求助开发支持任意层级文件夹创建的Component(含文件夹与子文件夹)
实现无限层级文件夹组件(支持任意层级创建)
核心思路
用递归数据结构存储文件夹层级,通过递归组件渲染嵌套结构,同时维护当前选中的文件夹ID,点击Add按钮时在选中节点下插入新文件夹。
1. 定义数据结构
每个文件夹节点包含唯一ID、名称、子节点数组,支持无限嵌套:
// 初始文件夹数据示例 const initialFolderData = [ { id: 'root-1', name: '项目文档', children: [ { id: 'sub-1', name: '需求分析', children: [] }, { id: 'sub-2', name: '设计稿', children: [ { id: 'sub-2-1', name: '移动端', children: [] } ] } ] }, { id: 'root-2', name: '开发资源', children: [] } ];
2. React组件实现(示例)
父组件(主容器)
负责维护全局状态(文件夹数据、选中ID),以及添加文件夹的核心逻辑:
import { useState } from 'react'; import FolderNode from './FolderNode'; export default function FolderTree() { const [folders, setFolders] = useState(initialFolderData); const [selectedFolderId, setSelectedFolderId] = useState(null); // 递归查找目标节点并添加子文件夹 const addSubFolder = (nodes, targetId) => { return nodes.map(node => { if (node.id === targetId) { return { ...node, children: [ ...node.children, { id: `${targetId}-${Date.now()}`, name: `新文件夹${node.children.length + 1}`, children: [] } ] }; } if (node.children.length > 0) { return { ...node, children: addSubFolder(node.children, targetId) }; } return node; }); }; const handleAddFolder = () => { if (!selectedFolderId) return; // 未选中时不执行 setFolders(prev => addSubFolder(prev, selectedFolderId)); }; return ( <div style={{ display: 'flex', gap: '20px', padding: '20px' }}> {/* 文件夹树区域 */} <div style={{ minWidth: '300px', border: '1px solid #eee', padding: '10px' }}> {folders.map(folder => ( <FolderNode key={folder.id} folder={folder} selectedId={selectedFolderId} onSelect={setSelectedFolderId} /> ))} </div> {/* 侧边操作栏 */} <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}> <button onClick={handleAddFolder} disabled={!selectedFolderId} style={{ padding: '8px 16px', cursor: 'pointer' }} > Add </button> </div> </div> ); }
递归子组件(FolderNode)
负责渲染单个文件夹节点,处理展开/折叠、选中状态:
import { useState } from 'react'; export default function FolderNode({ folder, selectedId, onSelect }) { const [isExpanded, setIsExpanded] = useState(true); const handleClick = () => { onSelect(folder.id); }; const toggleExpand = (e) => { e.stopPropagation(); // 避免触发选中事件 setIsExpanded(!isExpanded); }; return ( <div style={{ marginBottom: '8px' }}> {/* 文件夹头部 */} <div onClick={handleClick} style={{ padding: '4px 8px', backgroundColor: selectedId === folder.id ? '#e6f7ff' : 'transparent', borderRadius: '4px', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '6px' }} > {/* 展开/折叠图标 */} {folder.children.length > 0 && ( <span onClick={toggleExpand}> {isExpanded ? '▼' : '▶'} </span> )} <span>📁 {folder.name}</span> </div> {/* 渲染子文件夹 */} {isExpanded && folder.children.length > 0 && ( <div style={{ paddingLeft: '20px' }}> {folder.children.map(child => ( <FolderNode key={child.id} folder={child} selectedId={selectedId} onSelect={onSelect} /> ))} </div> )} </div> ); }
3. 关键功能说明
- 层级渲染:通过递归组件
FolderNode自动处理任意深度的文件夹嵌套,子节点通过paddingLeft实现视觉缩进。 - 选中状态管理:父组件维护
selectedFolderId,点击文件夹时更新该状态,选中项高亮显示。 - 添加文件夹逻辑:
addSubFolder函数递归遍历文件夹树,找到选中的节点后插入新的子文件夹,保证层级正确。 - 禁用逻辑:未选中任何文件夹时,Add按钮处于禁用状态,避免无效操作。
内容的提问来源于stack exchange,提问作者SerbianDeveloper
相关产品推荐
相关产品推荐

