在父级处理函数中获取react-konva上下文:是否有更优实现方式?
回答
两个问题的答案都是否,你当前的实现可以正常运行,但不属于react-konva的最优实践,存在耦合度高、可靠性不足的问题,有更简洁的实现方案。
关于问题(a)
完全不需要把子组件事件回传给父组件,才能获取stage/layer实例执行Konva节点操作。
Konva的事件对象本身就挂载了触发事件的原生Konva节点实例,不管事件处理逻辑写在子组件内部还是父组件,都可以直接通过e.target.getLayer()、e.target.getStage()拿到对应实例,随时可以调用find等原生Konva方法,不存在必须把事件往父组件冒泡传递才能操作节点的限制。
你当前把拖拽逻辑全写在父组件、再通过props把handler传给子组件的写法,会让父子组件强耦合。实际开发中子组件自身的交互逻辑完全可以在内部闭环,只有需要跨组件共享的业务数据才需要向上通知,不需要把整个事件对象透传出去。
关于问题(b)
完全不需要依赖find方法查找目标节点。
react-konva原生支持React标准的ref机制,可以直接拿到Konva节点实例,比find按name选择器查询的方式可靠很多:既不会因为存在同名节点拿到错误实例,也不受节点层级嵌套变动影响,是官方推荐的节点获取方式。
如果要更贴合React的开发习惯,甚至可以完全避免手动操作Konva节点实例:用组件state维护坐标、透明度、缩放这类属性,通过props传给对应Konva组件做响应式渲染,和写普通React组件的逻辑没有区别,可维护性更高。
优化后的实现代码
import { Stage, Layer, Text } from "react-konva"; import { KonvaEventObject } from "konva/lib/Node"; import { useRef, useState } from "react"; type TestTileProps = { id: string; // 只向外传递业务需要的坐标数据即可,不需要透传整个事件对象 onDragMove: (pos: { x: number; y: number }) => void; }; const TestTile = function ({ id, onDragMove }: TestTileProps) { const handleOnDragMove = (e: KonvaEventObject<DragEvent>) => { // 子组件内部可直接通过事件对象拿到节点、layer、stage实例 const currentPos = e.target.position(); // 仅抛出父组件需要的核心数据,不暴露Konva内部实例 onDragMove({ x: currentPos.x, y: currentPos.y }); }; return ( <Text y={20} text={id + " <=="} draggable={true} onDragMove={handleOnDragMove} /> ); }; const App = () => { // 方案1:通过ref直接持有目标节点引用,无需find查询 const mainTextRef = useRef<Konva.Text>(null); // 方案2:用state维护坐标,走React响应式渲染,无需手动操作节点 const [mainTextX, setMainTextX] = useState(0); const handleTileDrag = (pos: { x: number; y: number }) => { // 若用ref操作节点,打开下面注释即可 // mainTextRef.current?.x(pos.y); // 响应式写法:更新state自动触发重渲染 setMainTextX(pos.y); }; return ( <Stage width={window.innerWidth} height={window.innerHeight}> <Layer> <Text ref={mainTextRef} x={mainTextX} text="Heading 123" /> <TestTile id={"Value pass"} onDragMove={handleTileDrag} /> </Layer> </Stage> ); }; export default App;
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

