React输入框因重渲染失焦,如何改为顶层函数实现?
解决动态树形组件输入框失焦问题
问题原因
当前createItemsFromTree是父组件内部的函数,每次父组件重渲染时该函数都会被重新定义,导致它返回的React元素被识别为全新组件,输入框因此失去焦点。将其改为独立的顶层组件即可避免这个问题。
解决方案
把渲染树形节点的逻辑提取为独立的顶层组件,通过props传递所需的节点数据和回调函数,确保组件实例在重渲染时被复用。
修改后的代码示例
1. 提取为顶层组件
// 独立的树形节点组件,接收必要的props const TreeItemNode = ({ treeNode, handleCategoryInputChange, handleRemovecategory, handleAddSubCategory }) => { const hasChildren = treeNode.children?.length > 0; return ( <> <CustomTreeItem className={`json-display ${treeNode.placeholder}`} key={treeNode.id} label={ <div className="flex items-center gap-1 border border-[#e0dbdb] rounded-lg px-2 min-h-[36px] w-full my-[5px]"> <input type="text" className="outline-none text-[12px] w-full" placeholder={treeNode.parentId ? "Sub-Category" : "Category"} value={treeNode.name || ""} onChange={(e) => handleCategoryInputChange(treeNode.id, e.target.value) } /> {!treeNode.firstCategory && ( <Button icon={<FaTimes color="#adadad" size={14} />} onClick={() => handleRemovecategory(treeNode.id)} customClasses={`w-fit text-[10px] p-0 m-0 mx-0 mr-0 py-0 px-0 bg-transparent `} /> )} {hasChildren && ( <Button icon={<AiOutlinePlusSquare color="#bcbcbc" size={20} />} onClick={() => handleAddSubCategory(treeNode.id)} customClasses={`w-fit text-[10px] p-0 m-0 mx-0 mr-0 py-0 px-0 bg-transparent absolute translate-x-[12px]`} /> )} </div> } nodeId={treeNode.id} defaultExpanded={[treeNode.id, treeNode.parentId]} onClick={() => console.log(treeNode)} > {hasChildren && treeNode.children.map(child => ( <TreeItemNode key={child.id} treeNode={child} handleCategoryInputChange={handleCategoryInputChange} handleRemovecategory={handleRemovecategory} handleAddSubCategory={handleAddSubCategory} /> ))} </CustomTreeItem> {/* 无子女节点时显示添加按钮 */} {!hasChildren && ( <Button icon={<AiOutlinePlusSquare color="#bcbcbc" size={20} />} onClick={() => handleAddSubCategory(treeNode.id)} customClasses={`w-fit text-[10px] p-0 m-0 mx-0 mr-0 py-0 px-0 bg-transparent translate-y-[-23px] translate-x-[3px] absolute mr-[30px]`} /> )} </> ); };
2. 在父组件中调用该组件
<TreeView aria-label="file system navigator" expanded={expanded} onNodeToggle={handleToggle} defaultCollapseIcon={<ExpandMoreIcon />} defaultExpandIcon={<ChevronRightIcon />} sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto", }} > {categories?.map(category => ( <TreeItemNode key={category.id} treeNode={category} handleCategoryInputChange={handleCategoryInputChange} handleRemovecategory={handleRemovecategory} handleAddSubCategory={handleAddSubCategory} /> ))} </TreeView>
关键说明
- 提取后的
TreeItemNode是顶层组件,不会随父组件重渲染而重新创建,React会复用已有组件实例,输入框焦点得以保留 - 所有业务逻辑相关的回调函数和节点数据都通过props传递,保证组件内部能正常访问所需资源
- 合并了原代码中重复的
CustomTreeItem渲染逻辑,减少代码冗余,提升可维护性
内容的提问来源于stack exchange,提问作者Cool Dev
相关产品推荐
相关产品推荐

