/** * ReactFlow节点内部几何更新的Hook适配。将同一个store内的更新交给共享批处理器合并,卸载时释放节点租约。 */ "use client"; import {useCallback,useEffect,useRef} from 'react'; import {useStoreApi,useUpdateNodeInternals} from '@xyflow/react'; import {createNodeInternalsBatch} from '@/lib/canvas-bindings/node-internals'; // useStoreApi wraps the API per hook. Its shared getState function identifies the // actual store, separating canvases even when they render the same project. /** WeakMap按实际store的getState函数区分画布;不按projectId共享,避免多个视图误用另一视图的更新器。 */ const batches=new WeakMap>(); /** 返回稳定的invalidate回调;注册尚未建立或已卸载时调用无效,不直接触发布局读取。 */ export function useNodeInternalsUpdate(id:string){ // useStoreApi的包装对象可能每次不同,稳定getState函数作为store身份;update交给批处理器实际刷新。 const scope=useStoreApi().getState,update=useUpdateNodeInternals(); const lease=useRef['acquire']>|undefined>(undefined); // 首个节点创建store级batch,每次挂载取得本id的owner;清理仅释放该owner。 useEffect(()=>{ let batch=batches.get(scope);if(!batch){batch=createNodeInternalsBatch(update);batches.set(scope,batch);} const owner=batch.acquire(id);lease.current=owner; return()=>{owner.dispose();if(lease.current===owner)lease.current=undefined;}; },[scope,update,id]); // 回调通过ref读取当前租约,避免调用方保存旧闭包后继续刷新已卸载节点。 return useCallback(()=>lease.current?.invalidate(),[]); }