30 lines
2.0 KiB
TypeScript
30 lines
2.0 KiB
TypeScript
/**
|
|
* 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<object,ReturnType<typeof createNodeInternalsBatch>>();
|
|
/** 返回稳定的invalidate回调;注册尚未建立或已卸载时调用无效,不直接触发布局读取。 */
|
|
export function useNodeInternalsUpdate(id:string){
|
|
// useStoreApi的包装对象可能每次不同,稳定getState函数作为store身份;update交给批处理器实际刷新。
|
|
const scope=useStoreApi().getState,update=useUpdateNodeInternals();
|
|
const lease=useRef<ReturnType<ReturnType<typeof createNodeInternalsBatch>['acquire']>|undefined>(undefined);
|
|
// 首个节点创建store级batch,每次挂载取得本id的owner;清理仅释放该owner。
|
|
useEffect(()=>{
|
|
let batch=batches.get(scope);if(!batch){batch=createNodeInternalsBatch(update);batches.set(scope,batch);}
|
|
// batch 首次创建时捕获 update;同 store 后续使用者沿用该更新器,不在每次 effect 中替换。
|
|
const owner=batch.acquire(id);lease.current=owner;
|
|
return()=>{owner.dispose();if(lease.current===owner)lease.current=undefined;};
|
|
},[scope,update,id]);
|
|
// 回调通过ref读取当前租约;旧调用者保存的同一回调,在本组件 id 改变后会转向新 id 的租约。
|
|
// effect 尚未安装/已清理则无动作;它不把回调永久绑定到最初 id。
|
|
return useCallback(()=>lease.current?.invalidate(),[]);
|
|
}
|