React中如何实现树形结构下点击按钮条件显示文本字段?
在React中实现树形文件夹结构的动态输入字段添加
核心思路
- 用状态维护树形数据、选中节点和输入框的显示状态
- 递归渲染树形结构,处理节点选中事件
- 点击
new按钮时,定位到选中节点,触发其下方的输入框显示 - 输入完成后将新节点添加到树形数据中
步骤实现
1. 定义状态和初始树形数据
用useState管理树形数据、选中节点ID以及输入框的显示状态:
import { useState } from 'react'; const App = () => { // 初始树形数据,包含manufacturing文件夹 const [treeData, setTreeData] = useState([ { id: '1', name: 'Root', type: 'folder', children: [ { id: '2', name: 'manufacturing', type: 'folder', children: [], isSelected: false, showInput: false }, { id: '3', name: 'sales', type: 'folder', children: [], isSelected: false, showInput: false }, { id: '4', name: 'report.pdf', type: 'file', isSelected: false, showInput: false } ], isSelected: false, showInput: false } ]); const [selectedNodeId, setSelectedNodeId] = useState(null);
2. 递归渲染树形节点
编写递归组件TreeItem,负责单个节点的渲染、选中事件处理,以及输入框的条件渲染:
const TreeItem = ({ node }) => { // 处理节点选中逻辑 const handleNodeClick = () => { // 重置所有节点的选中状态 const resetSelection = (data) => { return data.map(item => ({ ...item, isSelected: false, children: item.children ? resetSelection(item.children) : [] })); }; const updatedData = resetSelection(treeData); // 标记当前节点为选中 const setSelected = (data) => { return data.map(item => { if (item.id === node.id) { return { ...item, isSelected: true }; } return { ...item, children: item.children ? setSelected(item.children) : [] }; }); }; setTreeData(setSelected(updatedData)); setSelectedNodeId(node.id); }; // 处理输入框确认(新建节点) const handleInputConfirm = (e) => { if (e.key === 'Enter' && e.target.value.trim()) { const addNewNode = (data) => { return data.map(item => { if (item.id === node.id) { return { ...item, showInput: false, children: [ ...item.children, { id: Date.now().toString(), name: e.target.value.trim(), type: 'folder', // 可根据需求改为file children: [], isSelected: false, showInput: false } ] }; } return { ...item, children: item.children ? addNewNode(item.children) : [] }; }); }; setTreeData(addNewNode(treeData)); e.target.value = ''; } }; return ( <div style={{ marginLeft: '20px' }}> <div onClick={handleNodeClick} style={{ cursor: 'pointer', fontWeight: node.isSelected ? 'bold' : 'normal', color: node.isSelected ? '#1890ff' : '#333' }} > {node.type === 'folder' ? '📁' : '📄'} {node.name} </div> {/* 条件渲染输入框 */} {node.showInput && ( <input type="text" placeholder="输入名称..." autoFocus onKeyDown={handleInputConfirm} onBlur={() => { // 失去焦点隐藏输入框 const hideInput = (data) => { return data.map(item => { if (item.id === node.id) { return { ...item, showInput: false }; } return { ...item, children: item.children ? hideInput(item.children) : [] }; }); }; setTreeData(hideInput(treeData)); }} style={{ marginLeft: '20px', marginTop: '5px' }} /> )} {/* 递归渲染子节点 */} {node.children && node.children.map(child => ( <TreeItem key={child.id} node={child} /> ))} </div> ); };
3. 处理New按钮点击事件
点击右上角new按钮时,找到选中节点并开启输入框:
const handleNewClick = () => { if (!selectedNodeId) return; // 未选中节点时不执行 const showInputForSelected = (data) => { return data.map(item => { if (item.id === selectedNodeId) { return { ...item, showInput: true }; } return { ...item, children: item.children ? showInputForSelected(item.children) : [] }; }); }; setTreeData(showInputForSelected(treeData)); };
4. 组合页面结构
将树形组件和New按钮整合到页面中:
return ( <div style={{ padding: '20px' }}> <div style={{ textAlign: 'right', marginBottom: '20px' }}> <button onClick={handleNewClick} style={{ padding: '8px 16px' }}> New </button> </div> {treeData.map(root => ( <TreeItem key={root.id} node={root} /> ))} </div> ); }; export default App;
关键说明
- 选中节点时会自动重置其他节点的选中状态,确保同一时间只有一个节点被选中
- 输入框支持按回车确认新建节点,失去焦点则自动隐藏
- 可根据需求调整新建节点的类型(文件/文件夹),或修改输入框的样式与交互逻辑
内容的提问来源于stack exchange,提问作者Anuradha Karaguppi
相关产品推荐
相关产品推荐

