Next.js中Balkan FamilyTree自定义EditUI报错:this.editUI.init不是函数
解决Balkan FamilyTree自定义EditUI时
init is not a function错误 核心问题分析
你遇到的错误本质是自定义的EditUI没有正确实现库要求的接口:要么init方法不存在,要么方法的this绑定丢失导致库调用时无法识别。另外Next.js的SSR环境也可能导致初始化时机不对,触发DOM相关错误。
解决方案1:TypeScript类实现(正确绑定this)
用箭头函数定义类方法,确保this始终指向类实例,避免绑定丢失:
class CustomEditForm { // 必须实现init方法,参数对应tree实例、节点数据、保存回调 init = (tree: any, nodeData: any, onSave: (updatedData: any) => void) => { // 自定义弹窗/表单逻辑,示例用原生DOM创建 const editModal = document.createElement('div'); editModal.style.cssText = `position:fixed; top:50%; left:50%; transform:translate(-50%,-50%); padding:2rem; background:#fff; border-radius:8px; box-shadow:0 2px 10px rgba(0,0,0,0.1); z-index:9999;`; editModal.innerHTML = ` <h3>编辑节点信息</h3> <div style="margin:1rem 0;"> <label>姓名:</label> <input type="text" value="${nodeData.name}" style="padding:0.5rem; width:200px;" id="edit-name" /> </div> <button style="padding:0.5rem 1rem; margin-right:1rem;" id="save-edit">保存</button> <button style="padding:0.5rem 1rem;" id="cancel-edit">取消</button> `; document.body.appendChild(editModal); // 保存逻辑 document.getElementById('save-edit')?.addEventListener('click', () => { const updatedName = (document.getElementById('edit-name') as HTMLInputElement).value; // 调用库的回调更新节点 onSave({ ...nodeData, name: updatedName }); editModal.remove(); }); // 取消逻辑 document.getElementById('cancel-edit')?.addEventListener('click', () => { editModal.remove(); }); }; // 可选:实现destroy方法用于清理资源 destroy = () => { const modal = document.querySelector('div[style*="z-index:9999"]'); modal?.remove(); }; }
解决方案2:函数式实现(返回包含init的对象)
如果用函数式写法,必须返回一个包含init方法的对象,而不是直接返回函数:
function createCustomEditUI() { return { init(tree, nodeData, onSave) { // 这里写和上面类实现一样的UI逻辑 }, destroy() { // 清理逻辑 } }; }
Next.js中正确初始化EditUI
因为Balkan FamilyTree依赖DOM,必须在客户端组件中初始化EditUI,避免SSR阶段执行:
'use client'; // 标记为客户端组件 import { useEffect, useRef } from 'react'; import FamilyTree from '@balkanjs/react-family-tree'; export default function FamilyTreePage() { const treeRef = useRef<any>(null); // 你的节点数据 const treeNodes = [/* 节点数组 */]; useEffect(() => { if (treeRef.current) { // 获取树实例 const treeInstance = treeRef.current.tree; // 绑定自定义EditUI treeInstance.editUI = new CustomEditForm(); // 如果用函数式就写:treeInstance.editUI = createCustomEditUI(); } }, []); return ( <div style={{ width: '100%', height: '800px' }}> <FamilyTree ref={treeRef} nodes={treeNodes} // 其他配置项(比如theme、layout等) /> </div> ); }
关键注意点
- 方法绑定:TS类中必须用箭头函数定义
init/destroy,否则库调用时this会指向undefined,导致找不到方法。 - 客户端初始化:Next.js SSR阶段没有DOM,必须在
useEffect或客户端组件中初始化EditUI,不能直接在组件顶层实例化。 - 接口匹配:自定义EditUI必须包含
init方法,参数要和库要求的一致(tree实例、节点数据、保存回调)。
内容的提问来源于stack exchange,提问作者Anuj TBE
相关产品推荐
相关产品推荐

